cucumber 3.1.2 → 8.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 (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1880 -1146
  3. data/CONTRIBUTING.md +220 -61
  4. data/README.md +143 -22
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +49 -53
  7. data/lib/autotest/discover.rb +3 -2
  8. data/lib/cucumber/cli/configuration.rb +32 -7
  9. data/lib/cucumber/cli/main.rb +16 -15
  10. data/lib/cucumber/cli/options.rb +111 -79
  11. data/lib/cucumber/cli/profile_loader.rb +45 -26
  12. data/lib/cucumber/cli/rerun_file.rb +1 -1
  13. data/lib/cucumber/configuration.rb +47 -31
  14. data/lib/cucumber/constantize.rb +3 -6
  15. data/lib/cucumber/deprecate.rb +32 -7
  16. data/lib/cucumber/errors.rb +5 -7
  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 +12 -0
  20. data/lib/cucumber/events/step_activated.rb +0 -5
  21. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  22. data/lib/cucumber/events/test_case_created.rb +12 -0
  23. data/lib/cucumber/events/test_case_ready.rb +12 -0
  24. data/lib/cucumber/events/test_run_finished.rb +2 -1
  25. data/lib/cucumber/events/test_step_created.rb +12 -0
  26. data/lib/cucumber/events/undefined_parameter_type.rb +9 -0
  27. data/lib/cucumber/events.rb +15 -8
  28. data/lib/cucumber/file_specs.rb +8 -7
  29. data/lib/cucumber/filters/activate_steps.rb +6 -3
  30. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  31. data/lib/cucumber/filters/prepare_world.rb +5 -9
  32. data/lib/cucumber/filters/quit.rb +1 -3
  33. data/lib/cucumber/filters/tag_limits/verifier.rb +3 -7
  34. data/lib/cucumber/filters/tag_limits.rb +1 -3
  35. data/lib/cucumber/filters.rb +1 -0
  36. data/lib/cucumber/formatter/ansicolor.rb +74 -86
  37. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  38. data/lib/cucumber/formatter/backtrace_filter.rb +10 -7
  39. data/lib/cucumber/formatter/console.rb +76 -68
  40. data/lib/cucumber/formatter/console_counts.rb +4 -9
  41. data/lib/cucumber/formatter/console_issues.rb +12 -4
  42. data/lib/cucumber/formatter/duration.rb +1 -1
  43. data/lib/cucumber/formatter/duration_extractor.rb +4 -1
  44. data/lib/cucumber/formatter/errors.rb +7 -0
  45. data/lib/cucumber/formatter/fanout.rb +3 -1
  46. data/lib/cucumber/formatter/html.rb +11 -598
  47. data/lib/cucumber/formatter/http_io.rb +152 -0
  48. data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -2
  49. data/lib/cucumber/formatter/interceptor.rb +11 -30
  50. data/lib/cucumber/formatter/io.rb +57 -13
  51. data/lib/cucumber/formatter/json.rb +119 -124
  52. data/lib/cucumber/formatter/junit.rb +75 -55
  53. data/lib/cucumber/formatter/message.rb +23 -0
  54. data/lib/cucumber/formatter/message_builder.rb +256 -0
  55. data/lib/cucumber/formatter/pretty.rb +370 -153
  56. data/lib/cucumber/formatter/progress.rb +31 -32
  57. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  58. data/lib/cucumber/formatter/query/hook_by_test_step.rb +32 -0
  59. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  60. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  61. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  62. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +42 -0
  63. data/lib/cucumber/formatter/rerun.rb +24 -4
  64. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  65. data/lib/cucumber/formatter/steps.rb +8 -6
  66. data/lib/cucumber/formatter/summary.rb +17 -8
  67. data/lib/cucumber/formatter/unicode.rb +18 -20
  68. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  69. data/lib/cucumber/formatter/usage.rb +18 -15
  70. data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
  71. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +14 -18
  72. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  73. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  74. data/lib/cucumber/glue/dsl.rb +29 -15
  75. data/lib/cucumber/glue/hook.rb +37 -11
  76. data/lib/cucumber/glue/invoke_in_world.rb +17 -22
  77. data/lib/cucumber/glue/proto_world.rb +47 -53
  78. data/lib/cucumber/glue/registry_and_more.rb +62 -17
  79. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  80. data/lib/cucumber/glue/snippet.rb +23 -22
  81. data/lib/cucumber/glue/step_definition.rb +48 -23
  82. data/lib/cucumber/glue/world_factory.rb +1 -1
  83. data/lib/cucumber/hooks.rb +12 -11
  84. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +4 -3
  85. data/lib/cucumber/multiline_argument/data_table.rb +143 -123
  86. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  87. data/lib/cucumber/multiline_argument.rb +4 -6
  88. data/lib/cucumber/platform.rb +5 -5
  89. data/lib/cucumber/rake/task.rb +34 -25
  90. data/lib/cucumber/rspec/disable_option_parser.rb +15 -11
  91. data/lib/cucumber/rspec/doubles.rb +3 -5
  92. data/lib/cucumber/running_test_case.rb +3 -53
  93. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  94. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  95. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  96. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  97. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  98. data/lib/cucumber/runtime/support_code.rb +16 -15
  99. data/lib/cucumber/runtime/user_interface.rb +10 -19
  100. data/lib/cucumber/runtime.rb +78 -76
  101. data/lib/cucumber/step_definition_light.rb +4 -3
  102. data/lib/cucumber/step_definitions.rb +2 -2
  103. data/lib/cucumber/step_match.rb +17 -20
  104. data/lib/cucumber/step_match_search.rb +5 -3
  105. data/lib/cucumber/term/ansicolor.rb +72 -48
  106. data/lib/cucumber/term/banner.rb +57 -0
  107. data/lib/cucumber/version +1 -1
  108. data/lib/cucumber.rb +3 -2
  109. data/lib/simplecov_setup.rb +1 -1
  110. metadata +279 -81
  111. data/lib/cucumber/core_ext/string.rb +0 -11
  112. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  113. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  114. data/lib/cucumber/formatter/cucumber.css +0 -286
  115. data/lib/cucumber/formatter/cucumber.sass +0 -247
  116. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  117. data/lib/cucumber/formatter/html_builder.rb +0 -121
  118. data/lib/cucumber/formatter/inline-js.js +0 -30
  119. data/lib/cucumber/formatter/jquery-min.js +0 -154
  120. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  121. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  122. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  123. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  124. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  125. 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,59 @@ 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, config.error_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
+
62
+ collect_snippet_data(test_step, @ast_lookup) if result.undefined?
63
+ @pending_step_matches << @matches[test_step.to_s] if result.pending?
61
64
  @failed_results << result if result.failed?
62
65
  end
63
66
 
64
67
  def on_test_case_finished(event)
65
68
  test_case = event.test_case
66
69
  result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
67
- @failed_test_cases << test_case if result.failed?
68
70
  @passed_test_cases << test_case if result.passed?
69
71
  @total_duration += DurationExtractor.new(result).result_duration
70
72
  end
@@ -77,28 +79,25 @@ module Cucumber
77
79
 
78
80
  private
79
81
 
82
+ def gherkin_document
83
+ @ast_lookup.gherkin_document(current_feature_uri)
84
+ end
85
+
80
86
  def print_summary
81
87
  print_elements(@pending_step_matches, :pending, 'steps')
82
88
  print_elements(@failed_results, :failed, 'steps')
83
89
  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)
90
+ print_snippets(config.to_hash)
91
+ print_passing_wip(config, @passed_test_cases, @ast_lookup)
93
92
  end
94
93
 
95
94
  CHARS = {
96
- :passed => '.',
97
- :failed => 'F',
98
- :undefined => 'U',
99
- :pending => 'P',
100
- :skipped => '-'
101
- }
95
+ passed: '.',
96
+ failed: 'F',
97
+ undefined: 'U',
98
+ pending: 'P',
99
+ skipped: '-'
100
+ }.freeze
102
101
 
103
102
  def progress(status)
104
103
  char = CHARS[status]
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/term/banner'
4
+
5
+ module Cucumber
6
+ module Formatter
7
+ class PublishBannerPrinter
8
+ include Term::Banner
9
+
10
+ def initialize(configuration)
11
+ return if configuration.publish_enabled?
12
+
13
+ configuration.on_event :test_run_finished do |_event|
14
+ display_publish_ad(configuration.error_stream)
15
+ end
16
+ end
17
+
18
+ # rubocop:disable Metrics/MethodLength
19
+ def display_publish_ad(io)
20
+ display_banner(
21
+ [
22
+ [
23
+ 'Share your Cucumber Report with your team at ',
24
+ link('https://reports.cucumber.io')
25
+ ],
26
+ '',
27
+ [
28
+ 'Command line option: ',
29
+ highlight('--publish')
30
+ ],
31
+ [
32
+ 'Environment variable: ',
33
+ highlight('CUCUMBER_PUBLISH_ENABLED'),
34
+ '=',
35
+ highlight('true')
36
+ ],
37
+ [
38
+ 'cucumber.yml: ',
39
+ highlight('default: --publish')
40
+ ],
41
+ '',
42
+ [
43
+ 'More information at ',
44
+ link('https://cucumber.io/docs/cucumber/environment-variables/')
45
+ ],
46
+ '',
47
+ [
48
+ 'To disable this message, specify ',
49
+ pre('CUCUMBER_PUBLISH_QUIET=true'),
50
+ ' or use the '
51
+ ],
52
+ [
53
+ pre('--publish-quiet'),
54
+ ' option. You can also add this to your ',
55
+ pre('cucumber.yml:')
56
+ ],
57
+ [pre('default: --publish-quiet')]
58
+ ],
59
+ io
60
+ )
61
+ end
62
+ # rubocop:enable Metrics/MethodLength
63
+
64
+ def highlight(text)
65
+ [text, :cyan]
66
+ end
67
+
68
+ def link(text)
69
+ [text, :cyan, :bold, :underline]
70
+ end
71
+
72
+ def pre(text)
73
+ [text, :bold]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,32 @@
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
+
17
+ raise TestStepUnknownError, "No hook found for #{test_step.id} }. Known: #{@hook_id_by_test_step_id.keys}"
18
+ end
19
+
20
+ private
21
+
22
+ def on_test_step_created(event)
23
+ @hook_id_by_test_step_id[event.test_step.id] = nil
24
+ end
25
+
26
+ def on_hook_test_step_created(event)
27
+ @hook_id_by_test_step_id[event.test_step.id] = event.hook.id
28
+ end
29
+ end
30
+ end
31
+ end
32
+ 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,42 @@
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
+
19
+ @attempts_by_test_case_id[test_case.id]
20
+ end
21
+
22
+ def test_case_started_id_by_test_case(test_case)
23
+ 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)
24
+
25
+ @test_case_started_id_by_test_case_id[test_case.id]
26
+ end
27
+
28
+ private
29
+
30
+ def on_test_case_created(event)
31
+ @attempts_by_test_case_id[event.test_case.id] = 0
32
+ @test_case_started_id_by_test_case_id[event.test_case.id] = nil
33
+ end
34
+
35
+ def on_test_case_started(event)
36
+ @attempts_by_test_case_id[event.test_case.id] += 1
37
+ @test_case_started_id_by_test_case_id[event.test_case.id] = @config.id_generator.new_id
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -8,17 +8,31 @@ module Cucumber
8
8
  include Formatter::Io
9
9
 
10
10
  def initialize(config)
11
- @io = ensure_io(config.out_stream)
11
+ @io = ensure_io(config.out_stream, config.error_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
+
19
+ add_to_failures(test_case)
20
+ else
21
+ unless @latest_failed_test_case.nil?
22
+ if @latest_failed_test_case != test_case
23
+ add_to_failures(@latest_failed_test_case)
24
+ @latest_failed_test_case = nil
25
+ elsif result.ok?(@config.strict)
26
+ @latest_failed_test_case = nil
27
+ end
28
+ end
29
+ @latest_failed_test_case = test_case unless result.ok?(@config.strict)
30
+ end
19
31
  end
20
32
  config.on_event :test_run_finished do
33
+ add_to_failures(@latest_failed_test_case) unless @latest_failed_test_case.nil?
21
34
  next if @failures.empty?
35
+
22
36
  @io.print file_failures.join("\n")
23
37
  end
24
38
  end
@@ -28,6 +42,12 @@ module Cucumber
28
42
  def file_failures
29
43
  @failures.map { |file, lines| [file, lines].join(':') }
30
44
  end
45
+
46
+ def add_to_failures(test_case)
47
+ location = test_case.location
48
+ @failures[location.file] ||= []
49
+ @failures[location.file] << location.lines.max unless @failures[location.file].include?(location.lines.max)
50
+ end
31
51
  end
32
52
  end
33
53
  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
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'cucumber/formatter/console'
4
+
3
5
  module Cucumber
4
6
  module Formatter
5
7
  # The formatter used for <tt>--format steps</tt>
6
8
  class Steps
9
+ include Console
7
10
  def initialize(runtime, path_or_io, options)
8
- @io = ensure_io(path_or_io)
11
+ @io = ensure_io(path_or_io, nil)
9
12
  @options = options
10
13
  @step_definition_files = collect_steps(runtime)
11
14
  end
@@ -24,8 +27,8 @@ module Cucumber
24
27
  sources = @step_definition_files[step_definition_file]
25
28
  source_indent = source_indent(sources)
26
29
  sources.sort.each do |file_colon_line, regexp_source|
27
- @io.print regexp_source.indent(2)
28
- @io.print " # #{file_colon_line}".indent(source_indent - regexp_source.unpack('U*').length)
30
+ @io.print indent(regexp_source, 2)
31
+ @io.print indent(" # #{file_colon_line}", source_indent - regexp_source.unpack('U*').length)
29
32
  @io.puts
30
33
  end
31
34
  @io.puts
@@ -35,15 +38,14 @@ module Cucumber
35
38
  end
36
39
 
37
40
  def collect_steps(runtime)
38
- runtime.step_definitions.inject({}) do |step_definitions, step_definition|
41
+ runtime.step_definitions.each_with_object({}) do |step_definition, step_definitions|
39
42
  step_definitions[step_definition.file] ||= []
40
43
  step_definitions[step_definition.file] << [step_definition.file_colon_line, step_definition.regexp_source]
41
- step_definitions
42
44
  end
43
45
  end
44
46
 
45
47
  def source_indent(sources)
46
- sources.map { |file_colon_line, regexp| regexp.size }.max + 1
48
+ sources.map { |_file_colon_line, regexp| regexp.size }.max + 1
47
49
  end
48
50
  end
49
51
  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, config.error_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,18 @@ 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
+
51
+ feature_name = gherkin_document(uri).feature.name
52
+ @io.puts unless @current_feature_uri.nil?
53
+ @io.puts feature_name
54
+ @current_feature_uri = uri
46
55
  end
47
56
 
48
57
  def print_test_case(test_case)
@@ -9,46 +9,44 @@ 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'
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.")
19
+ Cucumber::CODEPAGE = 'cp1252'.freeze
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
48
46
 
49
47
  Kernel.extend(self)
50
- STDOUT.extend(self)
51
- STDERR.extend(self)
48
+ $stdout.extend(self)
49
+ $stderr.extend(self)
52
50
  end
53
51
  end
54
52
  end
@@ -0,0 +1,17 @@
1
+ module Cucumber
2
+ module Formatter
3
+ class URLReporter
4
+ def initialize(io)
5
+ @io = io
6
+ end
7
+
8
+ def report(banner)
9
+ @io.puts(banner)
10
+ end
11
+ end
12
+
13
+ class NoReporter
14
+ def report(banner); end
15
+ end
16
+ end
17
+ end