@@ -327,6 +327,59 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value)
327
327
)
328
328
end
329
329
330
+ # @macro define_reader
331
+ # Configures how RSpec treats metadata passed as part of a shared example
332
+ # group definition. For example, given this shared example group definition:
333
+ #
334
+ # RSpec.shared_context "uses DB", :db => true do
335
+ # around(:example) do |ex|
336
+ # MyORM.transaction(:rollback => true, &ex)
337
+ # end
338
+ # end
339
+ #
340
+ # ...there are two ways RSpec can treat the `:db => true` metadata, each
341
+ # of which has a corresponding config option:
342
+ #
343
+ # 1. `:trigger_inclusion`: this shared context will be implicitly included
344
+ # in any groups (or examples) that have `:db => true` metadata.
345
+ # 2. `:apply_to_host_groups`: the metadata will be inherited by the metadata
346
+ # hash of all host groups and examples.
347
+ #
348
+ # `:trigger_inclusion` is the legacy behavior from before RSpec 3.5 but should
349
+ # be considered deprecated. Instead, you can explicitly include a group with
350
+ # `include_context`:
351
+ #
352
+ # RSpec.describe "My model" do
353
+ # include_context "uses DB"
354
+ # end
355
+ #
356
+ # ...or you can configure RSpec to include the context based on matching metadata
357
+ # using an API that mirrors configured module inclusion:
358
+ #
359
+ # RSpec.configure do |rspec|
360
+ # rspec.include_context "uses DB", :db => true
361
+ # end
362
+ #
363
+ # `:apply_to_host_groups` is a new feature of RSpec 3.5 and will be the only
364
+ # supported behavior in RSpec 4.
365
+ #
366
+ # @overload shared_context_metadata_behavior
367
+ # @return [:trigger_inclusion, :apply_to_host_groups] the configured behavior
368
+ # @overload shared_context_metadata_behavior=(value)
369
+ # @param value [:trigger_inclusion, :apply_to_host_groups] sets the configured behavior
370
+ define_reader :shared_context_metadata_behavior
371
+ # @see shared_context_metadata_behavior
372
+ def shared_context_metadata_behavior = ( value )
373
+ case value
374
+ when :trigger_inclusion , :apply_to_host_groups
375
+ @shared_context_metadata_behavior = value
376
+ else
377
+ raise ArgumentError , "Cannot set `RSpec.configuration." \
378
+ "shared_context_metadata_behavior` to `#{ value . inspect } `. Only " \
379
+ "`:trigger_inclusion` and `:apply_to_host_groups` are valid values."
380
+ end
381
+ end
382
+
330
383
# Record the start time of the spec suite to measure load time.
331
384
add_setting :start_time
332
385
@@ -352,6 +405,7 @@ def treat_symbols_as_metadata_keys_with_true_values=(_value)
352
405
attr_reader :backtrace_formatter , :ordering_manager , :loaded_spec_files
353
406
354
407
# rubocop:disable Metrics/AbcSize
408
+ # rubocop:disable Metrics/MethodLength
355
409
def initialize
356
410
# rubocop:disable Style/GlobalVars
357
411
@start_time = $_rspec_core_load_started_at || ::RSpec ::Core ::Time . now
@@ -398,9 +452,11 @@ def initialize
398
452
@threadsafe = true
399
453
@max_displayed_failure_line_count = 10
400
454
@world = World ::Null
455
+ @shared_context_metadata_behavior = :trigger_inclusion
401
456
402
457
define_built_in_hooks
403
458
end
459
+ # rubocop:enable Metrics/MethodLength
404
460
# rubocop:enable Metrics/AbcSize
405
461
406
462
# @private
@@ -1153,7 +1209,7 @@ def exclusion_filter
1153
1209
# end
1154
1210
#
1155
1211
# RSpec.configure do |config|
1156
- # config.include(UserHelpers) # included in all modules
1212
+ # config.include(UserHelpers) # included in all groups
1157
1213
# config.include(AuthenticationHelpers, :type => :request)
1158
1214
# end
1159
1215
#
@@ -1172,12 +1228,55 @@ def exclusion_filter
1172
1228
# example has a singleton example group containing just the one
1173
1229
# example.
1174
1230
#
1231
+ # @see #include_context
1175
1232
# @see #extend
1176
1233
# @see #prepend
1177
1234
def include ( mod , *filters )
1178
- meta = Metadata . build_hash_from ( filters , :warn_about_example_group_filtering )
1179
- @include_modules . append ( mod , meta )
1180
- on_existing_matching_groups ( meta ) { |group | safe_include ( mod , group ) }
1235
+ define_mixed_in_module ( mod , filters , @include_modules , :include ) do |group |
1236
+ safe_include ( mod , group )
1237
+ end
1238
+ end
1239
+
1240
+ # Tells RSpec to include the named shared example group in example groups.
1241
+ # Use `filters` to constrain the groups or examples in which to include
1242
+ # the example group.
1243
+ #
1244
+ # @example
1245
+ #
1246
+ # RSpec.shared_context "example users" do
1247
+ # let(:admin_user) { create_user(:admin) }
1248
+ # let(:guest_user) { create_user(:guest) }
1249
+ # end
1250
+ #
1251
+ # RSpec.configure do |config|
1252
+ # config.include_context "example users", :type => :request
1253
+ # end
1254
+ #
1255
+ # RSpec.describe "The admin page", :type => :request do
1256
+ # it "can be viewed by admins" do
1257
+ # login_with admin_user
1258
+ # get "/admin"
1259
+ # expect(response).to be_ok
1260
+ # end
1261
+ #
1262
+ # it "cannot be viewed by guests" do
1263
+ # login_with guest_user
1264
+ # get "/admin"
1265
+ # expect(response).to be_forbidden
1266
+ # end
1267
+ # end
1268
+ #
1269
+ # @note Filtered context inclusions can also be applied to
1270
+ # individual examples that have matching metadata. Just like
1271
+ # Ruby's object model is that every object has a singleton class
1272
+ # which has only a single instance, RSpec's model is that every
1273
+ # example has a singleton example group containing just the one
1274
+ # example.
1275
+ #
1276
+ # @see #include
1277
+ def include_context ( shared_group_name , *filters )
1278
+ shared_module = world . shared_example_group_registry . find ( [ :main ] , shared_group_name )
1279
+ include shared_module , *filters
1181
1280
end
1182
1281
1183
1282
# Tells RSpec to extend example groups with `mod`. Methods defined in
@@ -1211,9 +1310,9 @@ def include(mod, *filters)
1211
1310
# @see #include
1212
1311
# @see #prepend
1213
1312
def extend ( mod , *filters )
1214
- meta = Metadata . build_hash_from ( filters , :warn_about_example_group_filtering )
1215
- @extend_modules . append ( mod , meta )
1216
- on_existing_matching_groups ( meta ) { | group | safe_extend ( mod , group ) }
1313
+ define_mixed_in_module ( mod , filters , @extend_modules , :extend ) do | group |
1314
+ safe_extend ( mod , group )
1315
+ end
1217
1316
end
1218
1317
1219
1318
if RSpec ::Support ::RubyFeatures . module_prepends_supported?
@@ -1250,9 +1349,9 @@ def extend(mod, *filters)
1250
1349
# @see #include
1251
1350
# @see #extend
1252
1351
def prepend ( mod , *filters )
1253
- meta = Metadata . build_hash_from ( filters , :warn_about_example_group_filtering )
1254
- @prepend_modules . append ( mod , meta )
1255
- on_existing_matching_groups ( meta ) { | group | safe_prepend ( mod , group ) }
1352
+ define_mixed_in_module ( mod , filters , @prepend_modules , :prepend ) do | group |
1353
+ safe_prepend ( mod , group )
1354
+ end
1256
1355
end
1257
1356
end
1258
1357
@@ -1261,6 +1360,8 @@ def prepend(mod, *filters)
1261
1360
# Used internally to extend a group with modules using `include`, `prepend` and/or
1262
1361
# `extend`.
1263
1362
def configure_group ( group )
1363
+ group . hooks . register_globals ( group , hooks )
1364
+
1264
1365
configure_group_with group , @include_modules , :safe_include
1265
1366
configure_group_with group , @extend_modules , :safe_extend
1266
1367
configure_group_with group , @prepend_modules , :safe_prepend
@@ -1270,7 +1371,8 @@ def configure_group(group)
1270
1371
#
1271
1372
# Used internally to extend the singleton class of a single example's
1272
1373
# example group instance with modules using `include` and/or `extend`.
1273
- def configure_example ( example )
1374
+ def configure_example ( example , example_hooks )
1375
+ example_hooks . register_global_singleton_context_hooks ( example , hooks )
1274
1376
singleton_group = example . example_group_instance . singleton_class
1275
1377
1276
1378
# We replace the metadata so that SharedExampleGroupModule#included
@@ -1956,6 +2058,16 @@ def safe_extend(mod, host)
1956
2058
end
1957
2059
# :nocov:
1958
2060
end
2061
+
2062
+ def define_mixed_in_module ( mod , filters , mod_list , config_method , &block )
2063
+ unless Module === mod
2064
+ raise TypeError , "`RSpec.configuration.#{ config_method } ` expects a module but got: #{ mod . inspect } "
2065
+ end
2066
+
2067
+ meta = Metadata . build_hash_from ( filters , :warn_about_example_group_filtering )
2068
+ mod_list . append ( mod , meta )
2069
+ on_existing_matching_groups ( meta , &block )
2070
+ end
1959
2071
end
1960
2072
# rubocop:enable Metrics/ClassLength
1961
2073
end
0 commit comments