rspec-core 3.8.0 → 3.12.2

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 (41) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/Changelog.md +154 -0
  4. data/README.md +20 -20
  5. data/lib/rspec/core/bisect/fork_runner.rb +6 -2
  6. data/lib/rspec/core/bisect/server.rb +1 -1
  7. data/lib/rspec/core/bisect/utilities.rb +12 -1
  8. data/lib/rspec/core/configuration.rb +126 -30
  9. data/lib/rspec/core/did_you_mean.rb +46 -0
  10. data/lib/rspec/core/drb.rb +7 -0
  11. data/lib/rspec/core/example.rb +18 -5
  12. data/lib/rspec/core/example_group.rb +36 -16
  13. data/lib/rspec/core/example_status_persister.rb +2 -2
  14. data/lib/rspec/core/filter_manager.rb +1 -1
  15. data/lib/rspec/core/formatters/console_codes.rb +17 -9
  16. data/lib/rspec/core/formatters/documentation_formatter.rb +35 -3
  17. data/lib/rspec/core/formatters/exception_presenter.rb +40 -12
  18. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  19. data/lib/rspec/core/formatters/helpers.rb +9 -1
  20. data/lib/rspec/core/formatters/html_printer.rb +1 -3
  21. data/lib/rspec/core/formatters/html_snippet_extractor.rb +2 -2
  22. data/lib/rspec/core/formatters.rb +12 -2
  23. data/lib/rspec/core/hooks.rb +43 -21
  24. data/lib/rspec/core/invocations.rb +1 -1
  25. data/lib/rspec/core/memoized_helpers.rb +60 -15
  26. data/lib/rspec/core/metadata.rb +2 -3
  27. data/lib/rspec/core/metadata_filter.rb +1 -1
  28. data/lib/rspec/core/option_parser.rb +27 -13
  29. data/lib/rspec/core/ordering.rb +12 -1
  30. data/lib/rspec/core/pending.rb +8 -16
  31. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +2 -4
  32. data/lib/rspec/core/rake_task.rb +22 -2
  33. data/lib/rspec/core/reporter.rb +9 -1
  34. data/lib/rspec/core/runner.rb +16 -3
  35. data/lib/rspec/core/shared_example_group.rb +4 -2
  36. data/lib/rspec/core/version.rb +1 -1
  37. data/lib/rspec/core/world.rb +17 -5
  38. data/lib/rspec/core.rb +27 -0
  39. data.tar.gz.sig +0 -0
  40. metadata +21 -15
  41. metadata.gz.sig +0 -0
@@ -0,0 +1,46 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ # Wrapper around Ruby's `DidYouMean::SpellChecker` when available to provide file name suggestions.
5
+ class DidYouMean
6
+ attr_reader :relative_file_name
7
+
8
+ def initialize(relative_file_name)
9
+ @relative_file_name = relative_file_name
10
+ end
11
+
12
+ if defined?(::DidYouMean::SpellChecker)
13
+ # provide probable suggestions
14
+ def call
15
+ checker = ::DidYouMean::SpellChecker.new(:dictionary => Dir["spec/**/*.rb"])
16
+ probables = checker.correct(relative_file_name.sub('./', ''))[0..2]
17
+ return '' unless probables.any?
18
+
19
+ formats probables
20
+ end
21
+ else
22
+ # return a hint if API for ::DidYouMean::SpellChecker not supported
23
+ def call
24
+ "\nHint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files."
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def formats(probables)
31
+ rspec_format = probables.map { |s, _| "rspec ./#{s}" }
32
+ red_font(top_and_tail rspec_format)
33
+ end
34
+
35
+ def top_and_tail(rspec_format)
36
+ spaces = ' ' * 20
37
+ rspec_format.insert(0, ' - Did you mean?').join("\n#{spaces}") + "\n"
38
+ end
39
+
40
+ def red_font(mytext)
41
+ colorizer = ::RSpec::Core::Formatters::ConsoleCodes
42
+ colorizer.wrap mytext, :failure
43
+ end
44
+ end
45
+ end
46
+ end
@@ -51,6 +51,7 @@ module RSpec
51
51
  argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
52
52
 
53
53
  add_failure_exit_code(argv)
54
+ add_error_exit_code(argv)
54
55
  add_full_description(argv)
55
56
  add_filter(argv, :inclusion, @filter_manager.inclusions)
56
57
  add_filter(argv, :exclusion, @filter_manager.exclusions)
@@ -67,6 +68,12 @@ module RSpec
67
68
  argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
68
69
  end
69
70
 
71
+ def add_error_exit_code(argv)
72
+ return unless @submitted_options[:error_exit_code]
73
+
74
+ argv << "--error-exit-code" << @submitted_options[:error_exit_code].to_s
75
+ end
76
+
70
77
  def add_full_description(argv)
71
78
  return unless @submitted_options[:full_description]
72
79
 
@@ -203,10 +203,13 @@ module RSpec
203
203
  description, example_block
204
204
  )
205
205
 
206
+ config = RSpec.configuration
207
+ config.apply_derived_metadata_to(@metadata)
208
+
206
209
  # This should perhaps be done in `Metadata::ExampleHash.create`,
207
210
  # but the logic there has no knowledge of `RSpec.world` and we
208
211
  # want to keep it that way. It's easier to just assign it here.
209
- @metadata[:last_run_status] = RSpec.configuration.last_run_statuses[id]
212
+ @metadata[:last_run_status] = config.last_run_statuses[id]
210
213
 
211
214
  @example_group_instance = @exception = nil
212
215
  @clock = RSpec::Core::Time
@@ -228,8 +231,13 @@ module RSpec
228
231
  @example_group_class
229
232
  end
230
233
 
231
- alias_method :pending?, :pending
232
- alias_method :skipped?, :skip
234
+ def pending?
235
+ !!pending
236
+ end
237
+
238
+ def skipped?
239
+ !!skip
240
+ end
233
241
 
234
242
  # @api private
235
243
  # instance_execs the block passed to the constructor in the context of
@@ -251,6 +259,7 @@ module RSpec
251
259
  with_around_and_singleton_context_hooks do
252
260
  begin
253
261
  run_before_example
262
+ RSpec.current_scope = :example
254
263
  @example_group_instance.instance_exec(self, &@example_block)
255
264
 
256
265
  if pending?
@@ -270,6 +279,7 @@ module RSpec
270
279
  rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e
271
280
  set_exception(e)
272
281
  ensure
282
+ RSpec.current_scope = :after_example_hook
273
283
  run_after_example
274
284
  end
275
285
  end
@@ -367,7 +377,7 @@ module RSpec
367
377
 
368
378
  # @private
369
379
  def inspect
370
- @example.inspect.gsub('Example', 'ExampleProcsy')
380
+ @example.inspect.gsub('Example', 'Example::Procsy')
371
381
  end
372
382
  end
373
383
 
@@ -454,6 +464,7 @@ module RSpec
454
464
  end
455
465
 
456
466
  def with_around_example_hooks
467
+ RSpec.current_scope = :before_example_hook
457
468
  hooks.run(:around, :example, self) { yield }
458
469
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
459
470
  set_exception(e)
@@ -574,7 +585,9 @@ module RSpec
574
585
  # this indicates whether or not it now passes.
575
586
  attr_accessor :pending_fixed
576
587
 
577
- alias pending_fixed? pending_fixed
588
+ def pending_fixed?
589
+ !!pending_fixed
590
+ end
578
591
 
579
592
  # @return [Boolean] Indicates if the example was completely skipped
580
593
  # (typically done via `:skip` metadata or the `skip` method). Skipped examples
@@ -7,7 +7,7 @@ module RSpec
7
7
  # ExampleGroup and {Example} are the main structural elements of
8
8
  # rspec-core. Consider this example:
9
9
  #
10
- # describe Thing do
10
+ # RSpec.describe Thing do
11
11
  # it "does something" do
12
12
  # end
13
13
  # end
@@ -90,7 +90,7 @@ module RSpec
90
90
  # Returns the class or module passed to the `describe` method (or alias).
91
91
  # Returns nil if the subject is not a class or module.
92
92
  # @example
93
- # describe Thing do
93
+ # RSpec.describe Thing do
94
94
  # it "does something" do
95
95
  # described_class == Thing
96
96
  # end
@@ -111,16 +111,14 @@ module RSpec
111
111
  # @overload $1
112
112
  # @overload $1(&example_implementation)
113
113
  # @param example_implementation [Block] The implementation of the example.
114
- # @overload $1(doc_string, *metadata_keys, metadata={})
114
+ # @overload $1(doc_string, *metadata)
115
115
  # @param doc_string [String] The example's doc string.
116
- # @param metadata [Hash] Metadata for the example.
117
- # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
118
- # Will be transformed into hash entries with `true` values.
119
- # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
116
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
117
+ # Symbols will be transformed into hash entries with `true` values.
118
+ # @overload $1(doc_string, *metadata, &example_implementation)
120
119
  # @param doc_string [String] The example's doc string.
121
- # @param metadata [Hash] Metadata for the example.
122
- # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
123
- # Will be transformed into hash entries with `true` values.
120
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
121
+ # Symbols will be transformed into hash entries with `true` values.
124
122
  # @param example_implementation [Block] The implementation of the example.
125
123
  # @yield [Example] the example object
126
124
  # @example
@@ -139,6 +137,11 @@ module RSpec
139
137
  # $1 "does something" do |ex|
140
138
  # # ex is the Example object that contains metadata about the example
141
139
  # end
140
+ #
141
+ # @example
142
+ # $1 "does something", :slow, :load_factor => 100 do
143
+ # end
144
+ #
142
145
  def self.define_example_method(name, extra_options={})
143
146
  idempotently_define_singleton_method(name) do |*all_args, &block|
144
147
  desc, *args = *all_args
@@ -204,11 +207,10 @@ module RSpec
204
207
  # @overload $1
205
208
  # @overload $1(&example_group_definition)
206
209
  # @param example_group_definition [Block] The definition of the example group.
207
- # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
210
+ # @overload $1(doc_string, *metadata, &example_implementation)
208
211
  # @param doc_string [String] The group's doc string.
209
- # @param metadata [Hash] Metadata for the group.
210
- # @param metadata_keys [Array<Symbol>] Metadata tags for the group.
211
- # Will be transformed into hash entries with `true` values.
212
+ # @param metadata [Array<Symbol>, Hash] Metadata for the group.
213
+ # Symbols will be transformed into hash entries with `true` values.
212
214
  # @param example_group_definition [Block] The definition of the example group.
213
215
  #
214
216
  # Generates a subclass of this example group which inherits
@@ -223,12 +225,21 @@ module RSpec
223
225
  # do_something_before
224
226
  # end
225
227
  #
228
+ # before(:example, :clean_env) do
229
+ # env.clear!
230
+ # end
231
+ #
226
232
  # let(:thing) { Thing.new }
227
233
  #
228
234
  # $1 "attribute (of something)" do
229
235
  # # examples in the group get the before hook
230
236
  # # declared above, and can access `thing`
231
237
  # end
238
+ #
239
+ # $1 "needs additional setup", :clean_env, :implementation => JSON do
240
+ # # specifies that hooks with matching metadata
241
+ # # should be be run additionally
242
+ # end
232
243
  # end
233
244
  #
234
245
  # @see DSL#describe
@@ -424,11 +435,15 @@ module RSpec
424
435
  superclass.method(:next_runnable_index_for),
425
436
  description, *args, &example_group_block
426
437
  )
438
+
439
+ config = RSpec.configuration
440
+ config.apply_derived_metadata_to(@metadata)
441
+
427
442
  ExampleGroups.assign_const(self)
428
443
 
429
444
  @currently_executing_a_context_hook = false
430
445
 
431
- RSpec.configuration.configure_group(self)
446
+ config.configure_group(self)
432
447
  end
433
448
 
434
449
  # @private
@@ -587,6 +602,7 @@ module RSpec
587
602
 
588
603
  should_run_context_hooks = descendant_filtered_examples.any?
589
604
  begin
605
+ RSpec.current_scope = :before_context_hook
590
606
  run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
591
607
  result_for_this_group = run_examples(reporter)
592
608
  results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
@@ -599,6 +615,7 @@ module RSpec
599
615
  RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met?
600
616
  false
601
617
  ensure
618
+ RSpec.current_scope = :after_context_hook
602
619
  run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
603
620
  reporter.example_group_finished(self)
604
621
  end
@@ -686,6 +703,7 @@ module RSpec
686
703
  end
687
704
  end
688
705
 
706
+ # @private
689
707
  def initialize(inspect_output=nil)
690
708
  @__inspect_output = inspect_output || '(no description provided)'
691
709
  super() # no args get passed
@@ -746,8 +764,9 @@ module RSpec
746
764
  "on an example group (e.g. a `describe` or `context` block)."
747
765
  end
748
766
 
749
- super
767
+ super(name, *args)
750
768
  end
769
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
751
770
  end
752
771
  # rubocop:enable Metrics/ClassLength
753
772
 
@@ -766,6 +785,7 @@ module RSpec
766
785
  # @return [String] the location where the shared example was included
767
786
  attr_reader :inclusion_location
768
787
 
788
+ # @private
769
789
  def initialize(shared_group_name, inclusion_location)
770
790
  @shared_group_name = shared_group_name
771
791
  @inclusion_location = inclusion_location
@@ -63,7 +63,7 @@ module RSpec
63
63
  # were loaded but not executed (due to filtering, `--fail-fast`
64
64
  # or whatever) should have a `:status` of `UNKNOWN_STATUS`.
65
65
  #
66
- # This willl produce a new list that:
66
+ # This will produce a new list that:
67
67
  # - Will be missing examples from previous runs that we know for sure
68
68
  # no longer exist.
69
69
  # - Will have the latest known status for any examples that either
@@ -164,7 +164,7 @@ module RSpec
164
164
  end
165
165
 
166
166
  def formatted_value_rows
167
- @foramtted_value_rows ||= rows.map do |row|
167
+ @formatted_value_rows ||= rows.map do |row|
168
168
  formatted_row_from(row)
169
169
  end
170
170
  end
@@ -113,7 +113,7 @@ module RSpec
113
113
 
114
114
  # @private
115
115
  class FilterRules
116
- PROC_HEX_NUMBER = /0x[0-9a-f]+@/
116
+ PROC_HEX_NUMBER = /0x[0-9a-f]+@?/
117
117
  PROJECT_DIR = File.expand_path('.')
118
118
 
119
119
  attr_accessor :opposite
@@ -7,15 +7,23 @@ module RSpec
7
7
  # @private
8
8
  VT100_CODES =
9
9
  {
10
- :black => 30,
11
- :red => 31,
12
- :green => 32,
13
- :yellow => 33,
14
- :blue => 34,
15
- :magenta => 35,
16
- :cyan => 36,
17
- :white => 37,
18
- :bold => 1,
10
+ :black => 30,
11
+ :red => 31,
12
+ :green => 32,
13
+ :yellow => 33,
14
+ :blue => 34,
15
+ :magenta => 35,
16
+ :cyan => 36,
17
+ :white => 37,
18
+ :bold_black => '1;30',
19
+ :bold_red => '1;31',
20
+ :bold_green => '1;32',
21
+ :bold_yellow => '1;33',
22
+ :bold_blue => '1;34',
23
+ :bold_magenta => '1;35',
24
+ :bold_cyan => '1;36',
25
+ :bold_white => '1;37',
26
+ :bold => 1,
19
27
  }
20
28
  # @private
21
29
  VT100_CODE_VALUES = VT100_CODES.invert
@@ -6,12 +6,19 @@ module RSpec
6
6
  module Formatters
7
7
  # @private
8
8
  class DocumentationFormatter < BaseTextFormatter
9
- Formatters.register self, :example_group_started, :example_group_finished,
9
+ Formatters.register self, :example_started, :example_group_started, :example_group_finished,
10
10
  :example_passed, :example_pending, :example_failed
11
11
 
12
12
  def initialize(output)
13
13
  super
14
14
  @group_level = 0
15
+
16
+ @example_running = false
17
+ @messages = []
18
+ end
19
+
20
+ def example_started(_notification)
21
+ @example_running = true
15
22
  end
16
23
 
17
24
  def example_group_started(notification)
@@ -27,19 +34,44 @@ module RSpec
27
34
 
28
35
  def example_passed(passed)
29
36
  output.puts passed_output(passed.example)
37
+
38
+ flush_messages
39
+ @example_running = false
30
40
  end
31
41
 
32
42
  def example_pending(pending)
33
43
  output.puts pending_output(pending.example,
34
44
  pending.example.execution_result.pending_message)
45
+
46
+ flush_messages
47
+ @example_running = false
35
48
  end
36
49
 
37
50
  def example_failed(failure)
38
51
  output.puts failure_output(failure.example)
52
+
53
+ flush_messages
54
+ @example_running = false
55
+ end
56
+
57
+ def message(notification)
58
+ if @example_running
59
+ @messages << notification.message
60
+ else
61
+ output.puts "#{current_indentation}#{notification.message}"
62
+ end
39
63
  end
40
64
 
41
65
  private
42
66
 
67
+ def flush_messages
68
+ @messages.each do |message|
69
+ output.puts "#{current_indentation(1)}#{message}"
70
+ end
71
+
72
+ @messages.clear
73
+ end
74
+
43
75
  def passed_output(example)
44
76
  ConsoleCodes.wrap("#{current_indentation}#{example.description.strip}", :success)
45
77
  end
@@ -61,8 +93,8 @@ module RSpec
61
93
  @next_failure_index += 1
62
94
  end
63
95
 
64
- def current_indentation
65
- ' ' * @group_level
96
+ def current_indentation(offset=0)
97
+ ' ' * (@group_level + offset)
66
98
  end
67
99
  end
68
100
  end
@@ -43,7 +43,7 @@ module RSpec
43
43
 
44
44
  if RSpec::Support::RubyFeatures.supports_exception_cause?
45
45
  def formatted_cause(exception)
46
- last_cause = final_exception(exception)
46
+ last_cause = final_exception(exception, [exception])
47
47
  cause = []
48
48
 
49
49
  if exception.cause
@@ -51,11 +51,13 @@ module RSpec
51
51
  cause << '--- Caused by: ---'
52
52
  cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/
53
53
 
54
- encoded_string(last_cause.message.to_s).split("\n").each do |line|
54
+ encoded_string(exception_message_string(last_cause)).split("\n").each do |line|
55
55
  cause << " #{line}"
56
56
  end
57
57
 
58
- cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}")
58
+ unless last_cause.backtrace.nil? || last_cause.backtrace.empty?
59
+ cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}")
60
+ end
59
61
  end
60
62
 
61
63
  cause
@@ -81,7 +83,7 @@ module RSpec
81
83
 
82
84
  def fully_formatted_lines(failure_number, colorizer)
83
85
  lines = [
84
- description,
86
+ encoded_description(description),
85
87
  detail_formatter.call(example, colorizer),
86
88
  formatted_message_and_backtrace(colorizer),
87
89
  extra_detail_formatter.call(failure_number, colorizer),
@@ -96,7 +98,8 @@ module RSpec
96
98
 
97
99
  def final_exception(exception, previous=[])
98
100
  cause = exception.cause
99
- if cause && !previous.include?(cause)
101
+
102
+ if cause && Exception === cause && !previous.include?(cause)
100
103
  previous << cause
101
104
  final_exception(cause, previous)
102
105
  else
@@ -171,21 +174,31 @@ module RSpec
171
174
  lines
172
175
  end
173
176
 
177
+ # rubocop:disable Lint/RescueException
178
+ def exception_message_string(exception)
179
+ exception.message.to_s
180
+ rescue Exception => other
181
+ "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
182
+ end
183
+ # rubocop:enable Lint/RescueException
184
+
174
185
  def exception_lines
175
- lines = []
176
- lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
177
- encoded_string(exception.message.to_s).split("\n").each do |line|
178
- lines << (line.empty? ? line : " #{line}")
186
+ @exception_lines ||= begin
187
+ lines = []
188
+ lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
189
+ encoded_string(exception_message_string(exception)).split("\n").each do |line|
190
+ lines << (line.empty? ? line : " #{line}")
191
+ end
192
+ lines
179
193
  end
180
- lines
181
194
  end
182
195
 
183
196
  def extra_failure_lines
184
197
  @extra_failure_lines ||= begin
185
198
  lines = Array(example.metadata[:extra_failure_lines])
186
199
  unless lines.empty?
187
- lines.unshift('')
188
- lines.push('')
200
+ lines.unshift('') unless lines.first == ''
201
+ lines.push('') unless lines.last == ''
189
202
  end
190
203
  lines
191
204
  end
@@ -229,6 +242,10 @@ module RSpec
229
242
  line_regex = RSpec.configuration.in_project_source_dir_regex
230
243
  loaded_spec_files = RSpec.configuration.loaded_spec_files
231
244
 
245
+ exception_backtrace.reject! do |line|
246
+ line.start_with?("<internal:")
247
+ end
248
+
232
249
  exception_backtrace.find do |line|
233
250
  next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
234
251
  path = File.expand_path(line_path)
@@ -244,6 +261,17 @@ module RSpec
244
261
  end
245
262
  end
246
263
 
264
+ if String.method_defined?(:encoding)
265
+ def encoded_description(description)
266
+ return if description.nil?
267
+ encoded_string(description)
268
+ end
269
+ else # for 1.8.7
270
+ def encoded_description(description)
271
+ description
272
+ end
273
+ end
274
+
247
275
  def exception_backtrace
248
276
  exception.backtrace || []
249
277
  end
@@ -0,0 +1,23 @@
1
+ RSpec::Support.require_rspec_core "formatters/base_formatter"
2
+
3
+ module RSpec
4
+ module Core
5
+ module Formatters
6
+ # @private
7
+ class FailureListFormatter < BaseFormatter
8
+ Formatters.register self, :example_failed, :dump_profile, :message
9
+
10
+ def example_failed(failure)
11
+ output.puts "#{failure.example.location}:#{failure.example.description}"
12
+ end
13
+
14
+ # Discard profile and messages
15
+ #
16
+ # These outputs are not really relevant in the context of this failure
17
+ # list formatter.
18
+ def dump_profile(_profile); end
19
+ def message(_message); end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -86,7 +86,15 @@ module RSpec
86
86
  # @param string [String] word to be pluralized
87
87
  # @return [String] pluralized word
88
88
  def self.pluralize(count, string)
89
- "#{count} #{string}#{'s' unless count.to_f == 1}"
89
+ pluralized_string = if count.to_f == 1
90
+ string
91
+ elsif string.end_with?('s') # e.g. "process"
92
+ "#{string}es" # e.g. "processes"
93
+ else
94
+ "#{string}s"
95
+ end
96
+
97
+ "#{count} #{pluralized_string}"
90
98
  end
91
99
 
92
100
  # @api private
@@ -33,10 +33,8 @@ module RSpec
33
33
  "<span class='duration'>#{formatted_run_time}s</span></dd>"
34
34
  end
35
35
 
36
- # rubocop:disable Metrics/ParameterLists
37
36
  def print_example_failed(pending_fixed, description, run_time, failure_id,
38
37
  exception, extra_content)
39
- # rubocop:enable Metrics/ParameterLists
40
38
  formatted_run_time = "%.5f" % run_time
41
39
 
42
40
  @output.puts " <dd class=\"example #{pending_fixed ? 'pending_fixed' : 'failed'}\">"
@@ -215,7 +213,7 @@ function assign_display_style_for_group(classname, display_flag, subgroup_flag)
215
213
  }
216
214
  }
217
215
  EOF
218
- # rubocop:enable LineLength
216
+ # rubocop:enable Layout/LineLength
219
217
 
220
218
  GLOBAL_STYLES = <<-EOF
221
219
  #rspec-header {
@@ -43,7 +43,7 @@ module RSpec
43
43
  #
44
44
  # @param backtrace [String] the backtrace from a test failure
45
45
  # @return [String] highlighted code snippet indicating where the test
46
- # failure occured
46
+ # failure occurred
47
47
  #
48
48
  # @see #post_process
49
49
  def snippet(backtrace)
@@ -103,7 +103,7 @@ module RSpec
103
103
  #
104
104
  # @param highlighted [String] syntax-highlighted snippet surrounding the
105
105
  # offending line of code
106
- # @param offending_line [Fixnum] line where failure occured
106
+ # @param offending_line [Fixnum] line where failure occurred
107
107
  # @return [String] completed snippet
108
108
  def post_process(highlighted, offending_line)
109
109
  new_lines = []
@@ -1,4 +1,5 @@
1
1
  RSpec::Support.require_rspec_support "directory_maker"
2
+
2
3
  # ## Built-in Formatters
3
4
  #
4
5
  # * progress (default) - Prints dots for passing examples, `F` for failures, `*`
@@ -74,10 +75,11 @@ module RSpec::Core::Formatters
74
75
  autoload :JsonFormatter, 'rspec/core/formatters/json_formatter'
75
76
  autoload :BisectDRbFormatter, 'rspec/core/formatters/bisect_drb_formatter'
76
77
  autoload :ExceptionPresenter, 'rspec/core/formatters/exception_presenter'
78
+ autoload :FailureListFormatter, 'rspec/core/formatters/failure_list_formatter'
77
79
 
78
80
  # Register the formatter class
79
81
  # @param formatter_class [Class] formatter class to register
80
- # @param notifications [Symbol, ...] one or more notifications to be
82
+ # @param notifications [Array<Symbol>] one or more notifications to be
81
83
  # registered to the specified formatter
82
84
  #
83
85
  # @see RSpec::Core::Formatters::BaseFormatter
@@ -192,10 +194,16 @@ module RSpec::Core::Formatters
192
194
 
193
195
  def duplicate_formatter_exists?(new_formatter)
194
196
  @formatters.any? do |formatter|
195
- formatter.class == new_formatter.class && formatter.output == new_formatter.output
197
+ formatter.class == new_formatter.class &&
198
+ has_matching_output?(formatter, new_formatter)
196
199
  end
197
200
  end
198
201
 
202
+ def has_matching_output?(formatter, new_formatter)
203
+ return true unless formatter.respond_to?(:output) && new_formatter.respond_to?(:output)
204
+ formatter.output == new_formatter.output
205
+ end
206
+
199
207
  def existing_formatter_implements?(notification)
200
208
  @reporter.registered_listeners(notification).any?
201
209
  end
@@ -212,6 +220,8 @@ module RSpec::Core::Formatters
212
220
  JsonFormatter
213
221
  when 'bisect-drb'
214
222
  BisectDRbFormatter
223
+ when 'f', 'failures'
224
+ FailureListFormatter
215
225
  end
216
226
  end
217
227