rspec-core 3.12.2 → 3.13.5

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.
data/README.md CHANGED
@@ -116,6 +116,11 @@ pretty much the same as `shared_examples` and `include_examples`, providing
116
116
  more accurate naming when you share hooks, `let` declarations, helper methods,
117
117
  etc, but no examples.
118
118
 
119
+ If you want to reuse shared examples or contexts across your RSpec suite you can
120
+ define them in a stand alone _*.rb_ files (_spec/support/shared_examples/definition.rb_
121
+ for example). But you will have to manually `require` them (there is no autoloading of
122
+ _spec/support/_ directory unless you set it up yourself).
123
+
119
124
  ## Metadata
120
125
 
121
126
  rspec-core stores a metadata hash with every example and group, which
@@ -136,7 +136,7 @@ module RSpec
136
136
  end
137
137
 
138
138
  def get_expected_failures_for?(ids)
139
- ids_to_run = ids + failed_example_ids
139
+ ids_to_run = all_example_ids & (ids + failed_example_ids)
140
140
  notify(
141
141
  :bisect_individual_run_start,
142
142
  :command => shell_command.repro_command_from(ids_to_run),
@@ -102,6 +102,7 @@ module RSpec
102
102
  private
103
103
 
104
104
  def run_specs(run_descriptor)
105
+ # :nocov: - Executed in a forked process, by integration/bisect_spec
105
106
  $stdout = $stderr = @spec_output
106
107
  formatter = CaptureFormatter.new(run_descriptor.failed_example_ids)
107
108
 
@@ -125,6 +126,7 @@ module RSpec
125
126
  else
126
127
  @channel.send(latest_run_results)
127
128
  end
129
+ # :nocov:
128
130
  end
129
131
  end
130
132
 
@@ -453,11 +453,30 @@ module RSpec
453
453
  add_setting :threadsafe
454
454
 
455
455
  # @macro add_setting
456
- # Maximum count of failed source lines to display in the failure reports.
457
- # (default `10`).
456
+ # Maximum count of failed source lines to display in the failure reports
457
+ # (defaults to `10`).
458
458
  # return [Integer]
459
459
  add_setting :max_displayed_failure_line_count
460
460
 
461
+ # @macro full_cause_backtrace
462
+ # Display the full backtrace of an exceptions cause (defaults to `false`).
463
+ # return [Boolean]
464
+ add_setting :full_cause_backtrace
465
+
466
+ # @macro add_setting
467
+ # Format the output for pending examples. Can be set to:
468
+ # - :full (default) - pending examples appear similarly to failures
469
+ # - :no_backtrace - same as above, but with no backtrace
470
+ # - :skip - do not show the section at all
471
+ # return [Symbol]
472
+ add_read_only_setting :pending_failure_output
473
+ def pending_failure_output=(mode)
474
+ raise ArgumentError,
475
+ "`pending_failure_output` can be set to :full, :no_backtrace, " \
476
+ "or :skip" unless [:full, :no_backtrace, :skip].include?(mode)
477
+ @pending_failure_output = mode
478
+ end
479
+
461
480
  # Determines which bisect runner implementation gets used to run subsets
462
481
  # of the suite during a bisection. Your choices are:
463
482
  #
@@ -557,8 +576,10 @@ module RSpec
557
576
  @derived_metadata_blocks = FilterableItemRepository::QueryOptimized.new(:any?)
558
577
  @threadsafe = true
559
578
  @max_displayed_failure_line_count = 10
579
+ @full_cause_backtrace = false
560
580
  @world = World::Null
561
581
  @shared_context_metadata_behavior = :trigger_inclusion
582
+ @pending_failure_output = :full
562
583
 
563
584
  define_built_in_hooks
564
585
  end
@@ -1381,6 +1402,9 @@ module RSpec
1381
1402
  #
1382
1403
  # # included in examples with `:type => :request` metadata
1383
1404
  # config.include(AuthenticationHelpers, :type => :request)
1405
+ #
1406
+ # # included in examples where the `:type` metadata matches a proc condition
1407
+ # config.include(AuthenticationHelpers, :type => proc { |type, _metadata| [:request, :controller].include?(type) })
1384
1408
  # end
1385
1409
  #
1386
1410
  # describe "edit profile", :preferences, :type => :request do
@@ -1573,8 +1597,10 @@ module RSpec
1573
1597
  def requires=(paths)
1574
1598
  directories = ['lib', default_path].select { |p| File.directory? p }
1575
1599
  RSpec::Core::RubyProject.add_to_load_path(*directories)
1576
- paths.each { |path| load_file_handling_errors(:require, path) }
1577
- @requires += paths
1600
+ paths.each { |path|
1601
+ load_file_handling_errors(:require, path)
1602
+ @requires << path
1603
+ }
1578
1604
  end
1579
1605
 
1580
1606
  # @private
@@ -1743,8 +1769,9 @@ module RSpec
1743
1769
  # @private
1744
1770
  RAISE_ERROR_WARNING_NOTIFIER = lambda { |message| raise message }
1745
1771
 
1746
- # Turns warnings into errors. This can be useful when
1772
+ # Turns RSpec warnings into errors. This can be useful when
1747
1773
  # you want RSpec to run in a 'strict' no warning situation.
1774
+ # (Note this does not capture or raise on Ruby warnings).
1748
1775
  #
1749
1776
  # @example
1750
1777
  #
@@ -2120,6 +2147,13 @@ module RSpec
2120
2147
  suggestions = DidYouMean.new(relative_file).call
2121
2148
  reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.#{suggestions}")
2122
2149
  RSpec.world.wants_to_quit = true
2150
+ rescue SyntaxError => ex
2151
+ relative_file = Metadata.relative_path(file)
2152
+ reporter.notify_non_example_exception(
2153
+ ex,
2154
+ "While loading #{relative_file} a `raise SyntaxError` occurred, RSpec will now quit."
2155
+ )
2156
+ RSpec.world.rspec_is_quitting = true
2123
2157
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
2124
2158
  relative_file = Metadata.relative_path(file)
2125
2159
  reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.")
@@ -79,6 +79,10 @@ module RSpec
79
79
  # deprecation (or otherwise access the reporter).
80
80
  :deprecation_stream,
81
81
 
82
+ # In order for `RSpec.configuration.dry_run?` to return `true` during
83
+ # processing the `requires` option, it must be parsed before it.
84
+ :dry_run,
85
+
82
86
  # load paths depend on nothing, but must be set before `requires`
83
87
  # to support load-path-relative requires.
84
88
  :libs,
@@ -169,9 +173,11 @@ module RSpec
169
173
  def args_from_options_file(path)
170
174
  return [] unless path && File.exist?(path)
171
175
  config_string = options_file_as_erb_string(path)
172
- FlatMap.flat_map(config_string.split(/\n+/), &:shellsplit)
176
+ config_lines = config_string.split(/\n+/).reject { |s| s =~ /\A\s*#/ }
177
+ FlatMap.flat_map(config_lines, &:shellsplit)
173
178
  end
174
179
 
180
+ # :nocov:
175
181
  def options_file_as_erb_string(path)
176
182
  if RUBY_VERSION >= '2.6'
177
183
  ERB.new(File.read(path), :trim_mode => '-').result(binding)
@@ -179,6 +185,7 @@ module RSpec
179
185
  ERB.new(File.read(path), nil, '-').result(binding)
180
186
  end
181
187
  end
188
+ # :nocov:
182
189
 
183
190
  def custom_options_file
184
191
  command_line_options[:custom_options_file]
@@ -11,6 +11,7 @@ module RSpec
11
11
 
12
12
  if defined?(::DidYouMean::SpellChecker)
13
13
  # provide probable suggestions
14
+ # :nocov: - not installed on CI
14
15
  def call
15
16
  checker = ::DidYouMean::SpellChecker.new(:dictionary => Dir["spec/**/*.rb"])
16
17
  probables = checker.correct(relative_file_name.sub('./', ''))[0..2]
@@ -18,15 +19,19 @@ module RSpec
18
19
 
19
20
  formats probables
20
21
  end
22
+ # :nocov:
21
23
  else
22
24
  # return a hint if API for ::DidYouMean::SpellChecker not supported
25
+ # :nocov:
23
26
  def call
24
27
  "\nHint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files."
25
28
  end
29
+ # :nocov:
26
30
  end
27
31
 
28
32
  private
29
33
 
34
+ # :nocov:
30
35
  def formats(probables)
31
36
  rspec_format = probables.map { |s, _| "rspec ./#{s}" }
32
37
  red_font(top_and_tail rspec_format)
@@ -41,6 +46,7 @@ module RSpec
41
46
  colorizer = ::RSpec::Core::Formatters::ConsoleCodes
42
47
  colorizer.wrap mytext, :failure
43
48
  end
49
+ # :nocov:
44
50
  end
45
51
  end
46
52
  end
@@ -91,7 +91,7 @@ module RSpec
91
91
  def add_filter(argv, name, hash)
92
92
  hash.each_pair do |k, v|
93
93
  next if CONDITIONAL_FILTERS.include?(k)
94
- tag = name == :inclusion ? k.to_s : "~#{k}".dup
94
+ tag = name == :inclusion ? k.to_s.dup : "~#{k}".dup
95
95
  tag << ":#{v}" if v.is_a?(String)
96
96
  argv << "--tag" << tag
97
97
  end unless hash.empty?
@@ -98,7 +98,7 @@ module RSpec
98
98
  loaded_spec_files = RSpec.configuration.loaded_spec_files
99
99
 
100
100
  Metadata.ascending(metadata) do |meta|
101
- return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
101
+ break meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
102
102
  end
103
103
  end
104
104
  end
@@ -204,6 +204,7 @@ module RSpec
204
204
  # @private
205
205
  # @macro [attach] define_example_group_method
206
206
  # @!scope class
207
+ # @method $1
207
208
  # @overload $1
208
209
  # @overload $1(&example_group_definition)
209
210
  # @param example_group_definition [Block] The definition of the example group.
@@ -212,6 +213,7 @@ module RSpec
212
213
  # @param metadata [Array<Symbol>, Hash] Metadata for the group.
213
214
  # Symbols will be transformed into hash entries with `true` values.
214
215
  # @param example_group_definition [Block] The definition of the example group.
216
+ # @return [RSpec::Core::ExampleGroup]
215
217
  #
216
218
  # Generates a subclass of this example group which inherits
217
219
  # everything except the examples themselves.
@@ -312,6 +314,11 @@ module RSpec
312
314
  # @private
313
315
  # @macro [attach] define_nested_shared_group_method
314
316
  # @!scope class
317
+ # @method $1(name, *args, &block)
318
+ # @param name [String, Symbol] The name of the shared group to include.
319
+ # @param args [Array] Pass parameters to a shared example group
320
+ # @param block [Block] Additional context to pass to the shared group.
321
+ # @return [RSpec::Core::ExampleGroup]
315
322
  #
316
323
  # @see SharedExampleGroup
317
324
  def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
@@ -202,8 +202,8 @@ module RSpec
202
202
 
203
203
  def split_file_scoped_rules
204
204
  rules_dup = @rules.dup
205
- locations = rules_dup.delete(:locations) { Hash.new([]) }
206
- ids = rules_dup.delete(:ids) { Hash.new([]) }
205
+ locations = rules_dup.delete(:locations) { Hash.new { [] } }
206
+ ids = rules_dup.delete(:ids) { Hash.new { [] } }
207
207
 
208
208
  return locations, ids, self.class.new(rules_dup)
209
209
  end
@@ -56,7 +56,12 @@ module RSpec
56
56
  end
57
57
 
58
58
  unless last_cause.backtrace.nil? || last_cause.backtrace.empty?
59
- cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}")
59
+ lines = backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata)
60
+ lines = [lines[0]] unless RSpec.configuration.full_cause_backtrace # rubocop:disable Metrics/BlockNesting
61
+
62
+ lines.each do |line|
63
+ cause << (" #{line}")
64
+ end
60
65
  end
61
66
  end
62
67
 
@@ -175,11 +180,25 @@ module RSpec
175
180
  end
176
181
 
177
182
  # 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}."
183
+ # :nocov:
184
+ if SyntaxError.instance_methods.include?(:detailed_message)
185
+ def exception_message_string(exception)
186
+ case exception
187
+ when SyntaxError then exception.detailed_message.to_s
188
+ else
189
+ exception.message.to_s
190
+ end
191
+ rescue Exception => other
192
+ "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
193
+ end
194
+ else
195
+ def exception_message_string(exception)
196
+ exception.message.to_s
197
+ rescue Exception => other
198
+ "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
199
+ end
182
200
  end
201
+ # :nocov:
183
202
  # rubocop:enable Lint/RescueException
184
203
 
185
204
  def exception_lines
@@ -235,22 +254,25 @@ module RSpec
235
254
  rescue SnippetExtractor::NoSuchLineError
236
255
  ["Unable to find matching line in #{file_path}"]
237
256
  rescue SecurityError
257
+ # :nocov: - SecurityError is no longer produced starting in ruby 2.7
238
258
  ["Unable to read failed line"]
259
+ # :nocov:
239
260
  end
240
261
 
241
262
  def find_failed_line
242
263
  line_regex = RSpec.configuration.in_project_source_dir_regex
243
264
  loaded_spec_files = RSpec.configuration.loaded_spec_files
244
265
 
245
- exception_backtrace.reject! do |line|
246
- line.start_with?("<internal:")
247
- end
266
+ reduced_backtrace =
267
+ exception_backtrace.reject do |line|
268
+ line.start_with?("<internal:")
269
+ end
248
270
 
249
- exception_backtrace.find do |line|
271
+ reduced_backtrace.find do |line|
250
272
  next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
251
273
  path = File.expand_path(line_path)
252
274
  loaded_spec_files.include?(path) || path =~ line_regex
253
- end || exception_backtrace.first
275
+ end || reduced_backtrace.first
254
276
  end
255
277
 
256
278
  def formatted_message_and_backtrace(colorizer)
@@ -267,9 +289,11 @@ module RSpec
267
289
  encoded_string(description)
268
290
  end
269
291
  else # for 1.8.7
292
+ # :nocov:
270
293
  def encoded_description(description)
271
294
  description
272
295
  end
296
+ # :nocov:
273
297
  end
274
298
 
275
299
  def exception_backtrace
@@ -312,10 +336,14 @@ module RSpec
312
336
  ]
313
337
  }
314
338
  elsif @execution_result.status == :pending
315
- {
339
+ options = {
316
340
  :message_color => RSpec.configuration.pending_color,
317
341
  :detail_formatter => PENDING_DETAIL_FORMATTER
318
342
  }
343
+ if RSpec.configuration.pending_failure_output == :no_backtrace
344
+ options[:backtrace_formatter] = EmptyBacktraceFormatter
345
+ end
346
+ options
319
347
  end
320
348
  end
321
349
 
@@ -376,7 +404,7 @@ module RSpec
376
404
  failure = common_backtrace_truncater.with_truncated_backtrace(failure)
377
405
  presenter = ExceptionPresenter.new(failure, @example, options)
378
406
  presenter.fully_formatted_lines(
379
- "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
407
+ "#{"#{failure_number}." if failure_number}#{index + 1}",
380
408
  colorizer
381
409
  )
382
410
  end
@@ -93,7 +93,9 @@ module RSpec
93
93
  "# Couldn't get snippet for #{file}"
94
94
  end
95
95
  rescue SecurityError
96
+ # :nocov: - SecurityError is no longer produced starting in ruby 2.7
96
97
  "# Couldn't get snippet for #{file}"
98
+ # :nocov:
97
99
  end
98
100
 
99
101
  # @api private
@@ -32,15 +32,16 @@ module RSpec
32
32
  @output_hash[:summary_line] = summary.totals_line
33
33
  end
34
34
 
35
- def stop(notification)
36
- @output_hash[:examples] = notification.examples.map do |example|
37
- format_example(example).tap do |hash|
38
- e = example.exception
35
+ def stop(group_notification)
36
+ @output_hash[:examples] = group_notification.notifications.map do |notification|
37
+ format_example(notification.example).tap do |hash|
38
+ e = notification.example.exception
39
+
39
40
  if e
40
- hash[:exception] = {
41
+ hash[:exception] = {
41
42
  :class => e.class.name,
42
43
  :message => e.message,
43
- :backtrace => e.backtrace,
44
+ :backtrace => notification.formatted_backtrace,
44
45
  }
45
46
  end
46
47
  end
@@ -461,7 +461,7 @@ module RSpec
461
461
  # TODO: consider making this an error in RSpec 4. For SemVer reasons,
462
462
  # we are only warning in RSpec 3.
463
463
  RSpec.warn_with "WARNING: `around(:context)` hooks are not supported and " \
464
- "behave like `around(:example)."
464
+ "behave like `around(:example)`."
465
465
  end
466
466
 
467
467
  hook = HOOK_TYPES[position][scope].new(block, options)
@@ -47,7 +47,7 @@ module RSpec
47
47
  return nil if line == '-e:1'.freeze
48
48
  line
49
49
  rescue SecurityError
50
- # :nocov:
50
+ # :nocov: - SecurityError is no longer produced starting in ruby 2.7
51
51
  nil
52
52
  # :nocov:
53
53
  end
@@ -120,6 +120,8 @@ module RSpec::Core
120
120
  # @return [String] The list of pending examples, fully formatted in the
121
121
  # way that RSpec's built-in formatters emit.
122
122
  def fully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
123
+ return if RSpec.configuration.pending_failure_output == :skip
124
+
123
125
  formatted = "\nPending: (Failures listed here are expected and do not affect your suite's status)\n".dup
124
126
 
125
127
  pending_notifications.each_with_index do |notification, index|
@@ -185,7 +185,9 @@ module RSpec::Core
185
185
 
186
186
  parser.on('-w', '--warnings', 'Enable ruby warnings') do
187
187
  if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
188
+ # :nocov: on older Ruby without Warning
188
189
  Warning[:deprecated] = true
190
+ # :nocov:
189
191
  end
190
192
  $VERBOSE = true
191
193
  end
@@ -78,6 +78,30 @@ module RSpec
78
78
  end
79
79
  end
80
80
 
81
+ # @private
82
+ # A strategy which delays looking up the ordering until needed
83
+ class Delayed
84
+ def initialize(registry, name)
85
+ @registry = registry
86
+ @name = name
87
+ end
88
+
89
+ def order(list)
90
+ strategy.order(list)
91
+ end
92
+
93
+ private
94
+
95
+ def strategy
96
+ @strategy ||= lookup_strategy
97
+ end
98
+
99
+ def lookup_strategy
100
+ raise "Undefined ordering strategy #{@name.inspect}" unless @registry.has_strategy?(@name)
101
+ @registry.fetch(@name)
102
+ end
103
+ end
104
+
81
105
  # @private
82
106
  # Stores the different ordering strategies.
83
107
  class Registry
@@ -99,6 +123,10 @@ module RSpec
99
123
  @strategies.fetch(name, &fallback)
100
124
  end
101
125
 
126
+ def has_strategy?(name)
127
+ @strategies.key?(name)
128
+ end
129
+
102
130
  def register(sym, strategy)
103
131
  @strategies[sym] = strategy
104
132
  end
@@ -143,9 +171,20 @@ module RSpec
143
171
  :defined
144
172
  elsif order == 'recently-modified'
145
173
  :recently_modified
174
+ else
175
+ order.to_sym
146
176
  end
147
177
 
148
- register_ordering(:global, ordering_registry.fetch(ordering_name)) if ordering_name
178
+ if ordering_name
179
+ strategy =
180
+ if ordering_registry.has_strategy?(ordering_name)
181
+ ordering_registry.fetch(ordering_name)
182
+ else
183
+ Delayed.new(ordering_registry, ordering_name)
184
+ end
185
+
186
+ register_ordering(:global, strategy)
187
+ end
149
188
  end
150
189
 
151
190
  def force(hash)
@@ -15,13 +15,13 @@ module RSpec
15
15
  end
16
16
 
17
17
  def method_missing(name, *args, &block)
18
- output.send(name, *args, &block)
18
+ output.__send__(name, *args, &block)
19
19
  end
20
20
 
21
21
  # Redirect calls for IO interface methods
22
22
  IO.instance_methods(false).each do |method|
23
23
  define_method(method) do |*args, &block|
24
- output.send(method, *args, &block)
24
+ output.__send__(method, *args, &block)
25
25
  end
26
26
  end
27
27
  end
@@ -59,7 +59,7 @@ module RSpec
59
59
  # executed. If you need to consider hooks as pending as well you can use
60
60
  # the pending metadata as an alternative, e.g.
61
61
  # `it "does something", pending: "message"`.
62
- def pending(message=nil)
62
+ def pending(message=nil, &_block)
63
63
  current_example = RSpec.current_example
64
64
 
65
65
  if block_given?
@@ -47,6 +47,7 @@ module RSpec
47
47
 
48
48
  if RUBY_VERSION < "1.9.0" || Support::Ruby.jruby?
49
49
  # Run RSpec with a clean (empty) environment is not supported
50
+ # :nocov:
50
51
  def with_clean_environment=(_value)
51
52
  raise ArgumentError, "Running in a clean environment is not supported on Ruby versions before 1.9.0"
52
53
  end
@@ -55,6 +56,7 @@ module RSpec
55
56
  def with_clean_environment
56
57
  false
57
58
  end
59
+ # :nocov:
58
60
  else
59
61
  # Run RSpec with a clean (empty) environment.
60
62
  attr_accessor :with_clean_environment
@@ -172,10 +172,11 @@ module RSpec::Core
172
172
  # @private
173
173
  def finish
174
174
  close_after do
175
- stop
175
+ examples_notification = Notifications::ExamplesNotification.new(self)
176
+ stop(examples_notification)
176
177
  notify :start_dump, Notifications::NullNotification
177
- notify :dump_pending, Notifications::ExamplesNotification.new(self)
178
- notify :dump_failures, Notifications::ExamplesNotification.new(self)
178
+ notify :dump_pending, examples_notification
179
+ notify :dump_failures, examples_notification
179
180
  notify :deprecation_summary, Notifications::NullNotification
180
181
  unless mute_profile_output?
181
182
  notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples,
@@ -197,9 +198,9 @@ module RSpec::Core
197
198
  end
198
199
 
199
200
  # @private
200
- def stop
201
+ def stop(notification)
201
202
  @duration = (RSpec::Core::Time.now - @start).to_f if @start
202
- notify :stop, Notifications::ExamplesNotification.new(self)
203
+ notify :stop, notification
203
204
  end
204
205
 
205
206
  # @private
@@ -182,7 +182,11 @@ module RSpec
182
182
  exit!(1)
183
183
  else
184
184
  RSpec.world.wants_to_quit = true
185
- $stderr.puts "\nRSpec is shutting down and will print the summary report... Interrupt again to force quit."
185
+
186
+ $stderr.puts(
187
+ "\nRSpec is shutting down and will print the summary report... Interrupt again to force quit " \
188
+ "(warning: at_exit hooks will be skipped if you force quit)."
189
+ )
186
190
  end
187
191
  end
188
192
 
@@ -3,7 +3,7 @@ module RSpec
3
3
  # Version information for RSpec Core.
4
4
  module Version
5
5
  # Current version of RSpec Core, in semantic versioning format.
6
- STRING = '3.12.2'
6
+ STRING = '3.13.5'
7
7
  end
8
8
  end
9
9
  end
data.tar.gz.sig CHANGED
Binary file