rspec-core 3.1.7 → 3.2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +1 -0
- data/Changelog.md +84 -0
- data/README.md +10 -1
- data/lib/rspec/core.rb +28 -8
- data/lib/rspec/core/backport_random.rb +12 -9
- data/lib/rspec/core/configuration.rb +350 -112
- data/lib/rspec/core/configuration_options.rb +14 -7
- data/lib/rspec/core/dsl.rb +7 -4
- data/lib/rspec/core/example.rb +86 -50
- data/lib/rspec/core/example_group.rb +247 -86
- data/lib/rspec/core/filter_manager.rb +38 -93
- data/lib/rspec/core/flat_map.rb +4 -4
- data/lib/rspec/core/formatters.rb +10 -6
- data/lib/rspec/core/formatters/base_formatter.rb +7 -4
- data/lib/rspec/core/formatters/base_text_formatter.rb +12 -12
- data/lib/rspec/core/formatters/console_codes.rb +8 -7
- data/lib/rspec/core/formatters/deprecation_formatter.rb +5 -3
- data/lib/rspec/core/formatters/documentation_formatter.rb +10 -4
- data/lib/rspec/core/formatters/helpers.rb +6 -4
- data/lib/rspec/core/formatters/html_formatter.rb +13 -8
- data/lib/rspec/core/formatters/html_printer.rb +26 -10
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -7
- data/lib/rspec/core/formatters/protocol.rb +27 -18
- data/lib/rspec/core/formatters/snippet_extractor.rb +14 -7
- data/lib/rspec/core/hooks.rb +252 -211
- data/lib/rspec/core/memoized_helpers.rb +16 -16
- data/lib/rspec/core/metadata.rb +67 -28
- data/lib/rspec/core/metadata_filter.rb +151 -24
- data/lib/rspec/core/minitest_assertions_adapter.rb +5 -2
- data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
- data/lib/rspec/core/mocking_adapters/mocha.rb +8 -8
- data/lib/rspec/core/notifications.rb +155 -94
- data/lib/rspec/core/option_parser.rb +16 -10
- data/lib/rspec/core/pending.rb +11 -9
- data/lib/rspec/core/project_initializer.rb +1 -1
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +10 -8
- data/lib/rspec/core/rake_task.rb +37 -52
- data/lib/rspec/core/reporter.rb +30 -7
- data/lib/rspec/core/ruby_project.rb +12 -4
- data/lib/rspec/core/runner.rb +5 -8
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/shared_example_group.rb +41 -15
- data/lib/rspec/core/test_unit_assertions_adapter.rb +3 -3
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +2 -2
- data/lib/rspec/core/world.rb +12 -28
- metadata +44 -31
- metadata.gz.sig +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Core
|
3
3
|
module Formatters
|
4
|
-
# Formatters helpers
|
4
|
+
# Formatters helpers.
|
5
5
|
module Helpers
|
6
6
|
# @private
|
7
7
|
SUB_SECOND_PRECISION = 5
|
@@ -39,8 +39,9 @@ module RSpec
|
|
39
39
|
|
40
40
|
# @api private
|
41
41
|
#
|
42
|
-
# Formats seconds to have 5 digits of precision with trailing zeros
|
43
|
-
# is less than 1 or with 2 digits of precision if
|
42
|
+
# Formats seconds to have 5 digits of precision with trailing zeros
|
43
|
+
# removed if the number is less than 1 or with 2 digits of precision if
|
44
|
+
# the number is greater than zero.
|
44
45
|
#
|
45
46
|
# @param float [Float]
|
46
47
|
# @return [String] formatted float
|
@@ -50,7 +51,8 @@ module RSpec
|
|
50
51
|
# format_seconds(0.020000) #=> "0.02"
|
51
52
|
# format_seconds(1.00000000001) #=> "1"
|
52
53
|
#
|
53
|
-
# The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
|
54
|
+
# The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
|
55
|
+
# {Helpers::DEFAULT_PRECISION}.
|
54
56
|
#
|
55
57
|
# @see #strip_trailing_zeroes
|
56
58
|
def self.format_seconds(float, precision=nil)
|
@@ -31,7 +31,9 @@ module RSpec
|
|
31
31
|
@example_group_number += 1
|
32
32
|
|
33
33
|
@printer.print_example_group_end unless example_group_number == 1
|
34
|
-
@printer.print_example_group_start(example_group_number,
|
34
|
+
@printer.print_example_group_start(example_group_number,
|
35
|
+
notification.group.description,
|
36
|
+
notification.group.parent_groups.size)
|
35
37
|
@printer.flush
|
36
38
|
end
|
37
39
|
|
@@ -111,15 +113,16 @@ module RSpec
|
|
111
113
|
|
112
114
|
private
|
113
115
|
|
114
|
-
# If these methods are declared with attr_reader Ruby will issue a
|
116
|
+
# If these methods are declared with attr_reader Ruby will issue a
|
117
|
+
# warning because they are private.
|
115
118
|
# rubocop:disable Style/TrivialAccessors
|
116
119
|
|
117
|
-
# The number of the currently running example_group
|
120
|
+
# The number of the currently running example_group.
|
118
121
|
def example_group_number
|
119
122
|
@example_group_number
|
120
123
|
end
|
121
124
|
|
122
|
-
# The number of the currently running example (a global counter)
|
125
|
+
# The number of the currently running example (a global counter).
|
123
126
|
def example_number
|
124
127
|
@example_number
|
125
128
|
end
|
@@ -133,12 +136,14 @@ module RSpec
|
|
133
136
|
result
|
134
137
|
end
|
135
138
|
|
136
|
-
# Override this method if you wish to output extra HTML for a failed
|
137
|
-
# could output links to images or other files
|
138
|
-
#
|
139
|
+
# Override this method if you wish to output extra HTML for a failed
|
140
|
+
# spec. For example, you could output links to images or other files
|
141
|
+
# produced during the specs.
|
139
142
|
def extra_failure_content(failure)
|
140
143
|
RSpec::Support.require_rspec_core "formatters/snippet_extractor"
|
141
|
-
backtrace = failure.exception.backtrace.map
|
144
|
+
backtrace = failure.exception.backtrace.map do |line|
|
145
|
+
RSpec.configuration.backtrace_formatter.backtrace_line(line)
|
146
|
+
end
|
142
147
|
backtrace.compact!
|
143
148
|
@snippet_extractor ||= SnippetExtractor.new
|
144
149
|
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
|
@@ -5,7 +5,7 @@ module RSpec
|
|
5
5
|
module Formatters
|
6
6
|
# @private
|
7
7
|
class HtmlPrinter
|
8
|
-
include ERB::Util #
|
8
|
+
include ERB::Util # For the #h method.
|
9
9
|
def initialize(output)
|
10
10
|
@output = output
|
11
11
|
end
|
@@ -28,11 +28,14 @@ module RSpec
|
|
28
28
|
|
29
29
|
def print_example_passed(description, run_time)
|
30
30
|
formatted_run_time = "%.5f" % run_time
|
31
|
-
@output.puts " <dd class=\"example passed\"
|
31
|
+
@output.puts " <dd class=\"example passed\">" \
|
32
|
+
"<span class=\"passed_spec_name\">#{h(description)}</span>" \
|
33
|
+
"<span class='duration'>#{formatted_run_time}s</span></dd>"
|
32
34
|
end
|
33
35
|
|
34
36
|
# rubocop:disable Style/ParameterLists
|
35
|
-
def print_example_failed(pending_fixed, description, run_time, failure_id,
|
37
|
+
def print_example_failed(pending_fixed, description, run_time, failure_id,
|
38
|
+
exception, extra_content, escape_backtrace=false)
|
36
39
|
# rubocop:enable Style/ParameterLists
|
37
40
|
formatted_run_time = "%.5f" % run_time
|
38
41
|
|
@@ -54,7 +57,9 @@ module RSpec
|
|
54
57
|
end
|
55
58
|
|
56
59
|
def print_example_pending(description, pending_message)
|
57
|
-
@output.puts " <dd class=\"example not_implemented\"
|
60
|
+
@output.puts " <dd class=\"example not_implemented\">" \
|
61
|
+
"<span class=\"not_implemented_spec_name\">#{h(description)} " \
|
62
|
+
"(PENDING: #{h(pending_message)})</span></dd>"
|
58
63
|
end
|
59
64
|
|
60
65
|
def print_summary(duration, example_count, failure_count, pending_count)
|
@@ -64,8 +69,11 @@ module RSpec
|
|
64
69
|
|
65
70
|
formatted_duration = "%.5f" % duration
|
66
71
|
|
67
|
-
@output.puts "<script type=\"text/javascript\">
|
68
|
-
|
72
|
+
@output.puts "<script type=\"text/javascript\">" \
|
73
|
+
"document.getElementById('duration').innerHTML = \"Finished in " \
|
74
|
+
"<strong>#{formatted_duration} seconds</strong>\";</script>"
|
75
|
+
@output.puts "<script type=\"text/javascript\">" \
|
76
|
+
"document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
|
69
77
|
@output.puts "</div>"
|
70
78
|
@output.puts "</div>"
|
71
79
|
@output.puts "</body>"
|
@@ -90,13 +98,17 @@ module RSpec
|
|
90
98
|
end
|
91
99
|
|
92
100
|
def make_example_group_header_red(group_id)
|
93
|
-
@output.puts " <script type=\"text/javascript\">
|
94
|
-
|
101
|
+
@output.puts " <script type=\"text/javascript\">" \
|
102
|
+
"makeRed('div_group_#{group_id}');</script>"
|
103
|
+
@output.puts " <script type=\"text/javascript\">" \
|
104
|
+
"makeRed('example_group_#{group_id}');</script>"
|
95
105
|
end
|
96
106
|
|
97
107
|
def make_example_group_header_yellow(group_id)
|
98
|
-
@output.puts " <script type=\"text/javascript\">
|
99
|
-
|
108
|
+
@output.puts " <script type=\"text/javascript\">" \
|
109
|
+
"makeYellow('div_group_#{group_id}');</script>"
|
110
|
+
@output.puts " <script type=\"text/javascript\">" \
|
111
|
+
"makeYellow('example_group_#{group_id}');</script>"
|
100
112
|
end
|
101
113
|
|
102
114
|
private
|
@@ -105,6 +117,7 @@ module RSpec
|
|
105
117
|
"style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
|
106
118
|
end
|
107
119
|
|
120
|
+
# rubocop:disable LineLength
|
108
121
|
REPORT_HEADER = <<-EOF
|
109
122
|
<div class="rspec-report">
|
110
123
|
|
@@ -128,7 +141,9 @@ module RSpec
|
|
128
141
|
|
129
142
|
<div class="results">
|
130
143
|
EOF
|
144
|
+
# rubocop:enable LineLength
|
131
145
|
|
146
|
+
# rubocop:disable LineLength
|
132
147
|
GLOBAL_SCRIPTS = <<-EOF
|
133
148
|
|
134
149
|
function addClass(element_id, classname) {
|
@@ -205,6 +220,7 @@ function assign_display_style_for_group(classname, display_flag, subgroup_flag)
|
|
205
220
|
}
|
206
221
|
}
|
207
222
|
EOF
|
223
|
+
# rubocop:enable LineLength
|
208
224
|
|
209
225
|
GLOBAL_STYLES = <<-EOF
|
210
226
|
#rspec-header {
|
@@ -4,7 +4,7 @@ module RSpec
|
|
4
4
|
module Core
|
5
5
|
module Formatters
|
6
6
|
# @api private
|
7
|
-
# Formatter for providing profile output
|
7
|
+
# Formatter for providing profile output.
|
8
8
|
class ProfileFormatter
|
9
9
|
Formatters.register self, :dump_profile
|
10
10
|
|
@@ -15,14 +15,13 @@ module RSpec
|
|
15
15
|
# @private
|
16
16
|
attr_reader :output
|
17
17
|
|
18
|
-
# @method dump_profile
|
19
18
|
# @api public
|
20
19
|
#
|
21
20
|
# This method is invoked after the dumping the summary if profiling is
|
22
21
|
# enabled.
|
23
22
|
#
|
24
|
-
# @param profile [ProfileNotification] containing duration,
|
25
|
-
#
|
23
|
+
# @param profile [ProfileNotification] containing duration,
|
24
|
+
# slowest_examples and slowest_example_groups
|
26
25
|
def dump_profile(profile)
|
27
26
|
dump_profile_slowest_examples(profile)
|
28
27
|
dump_profile_slowest_example_groups(profile)
|
@@ -31,11 +30,14 @@ module RSpec
|
|
31
30
|
private
|
32
31
|
|
33
32
|
def dump_profile_slowest_examples(profile)
|
34
|
-
@output.puts "\nTop #{profile.slowest_examples.size} slowest
|
33
|
+
@output.puts "\nTop #{profile.slowest_examples.size} slowest " \
|
34
|
+
"examples (#{Helpers.format_seconds(profile.slow_duration)} " \
|
35
|
+
"seconds, #{profile.percentage}% of total time):\n"
|
35
36
|
|
36
37
|
profile.slowest_examples.each do |example|
|
37
38
|
@output.puts " #{example.full_description}"
|
38
|
-
@output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))}
|
39
|
+
@output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))} " \
|
40
|
+
"#{bold("seconds")} #{format_caller(example.location)}"
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -53,7 +55,8 @@ module RSpec
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def format_caller(caller_info)
|
56
|
-
RSpec.configuration.backtrace_formatter.backtrace_line(
|
58
|
+
RSpec.configuration.backtrace_formatter.backtrace_line(
|
59
|
+
caller_info.to_s.split(':in `block').first)
|
57
60
|
end
|
58
61
|
|
59
62
|
def bold(text)
|
@@ -39,12 +39,14 @@ module RSpec
|
|
39
39
|
# @api public
|
40
40
|
# @group Group Notifications
|
41
41
|
#
|
42
|
-
# This method is invoked at the beginning of the execution of each
|
42
|
+
# This method is invoked at the beginning of the execution of each
|
43
|
+
# example group.
|
43
44
|
#
|
44
45
|
# The next method to be invoked after this is {#example_passed},
|
45
46
|
# {#example_pending}, or {#example_group_finished}.
|
46
47
|
#
|
47
|
-
# @param notification [GroupNotification] containing example_group
|
48
|
+
# @param notification [GroupNotification] containing example_group
|
49
|
+
# subclass of `RSpec::Core::ExampleGroup`
|
48
50
|
|
49
51
|
# @method example_group_finished
|
50
52
|
# @api public
|
@@ -52,7 +54,8 @@ module RSpec
|
|
52
54
|
#
|
53
55
|
# Invoked at the end of the execution of each example group.
|
54
56
|
#
|
55
|
-
# @param notification [GroupNotification] containing example_group
|
57
|
+
# @param notification [GroupNotification] containing example_group
|
58
|
+
# subclass of `RSpec::Core::ExampleGroup`
|
56
59
|
|
57
60
|
# @method example_started
|
58
61
|
# @api public
|
@@ -60,7 +63,8 @@ module RSpec
|
|
60
63
|
#
|
61
64
|
# Invoked at the beginning of the execution of each example.
|
62
65
|
#
|
63
|
-
# @param notification [ExampleNotification] containing example subclass
|
66
|
+
# @param notification [ExampleNotification] containing example subclass
|
67
|
+
# of `RSpec::Core::Example`
|
64
68
|
|
65
69
|
# @method example_passed
|
66
70
|
# @api public
|
@@ -68,7 +72,8 @@ module RSpec
|
|
68
72
|
#
|
69
73
|
# Invoked when an example passes.
|
70
74
|
#
|
71
|
-
# @param notification [ExampleNotification] containing example subclass
|
75
|
+
# @param notification [ExampleNotification] containing example subclass
|
76
|
+
# of `RSpec::Core::Example`
|
72
77
|
|
73
78
|
# @method example_pending
|
74
79
|
# @api public
|
@@ -76,7 +81,8 @@ module RSpec
|
|
76
81
|
#
|
77
82
|
# Invoked when an example is pending.
|
78
83
|
#
|
79
|
-
# @param notification [ExampleNotification] containing example subclass
|
84
|
+
# @param notification [ExampleNotification] containing example subclass
|
85
|
+
# of `RSpec::Core::Example`
|
80
86
|
|
81
87
|
# @method example_failed
|
82
88
|
# @api public
|
@@ -84,7 +90,8 @@ module RSpec
|
|
84
90
|
#
|
85
91
|
# Invoked when an example fails.
|
86
92
|
#
|
87
|
-
# @param notification [ExampleNotification] containing example subclass
|
93
|
+
# @param notification [ExampleNotification] containing example subclass
|
94
|
+
# of `RSpec::Core::Example`
|
88
95
|
|
89
96
|
# @method message
|
90
97
|
# @api public
|
@@ -98,7 +105,8 @@ module RSpec
|
|
98
105
|
# @api public
|
99
106
|
# @group Suite Notifications
|
100
107
|
#
|
101
|
-
# Invoked after all examples have executed, before dumping post-run
|
108
|
+
# Invoked after all examples have executed, before dumping post-run
|
109
|
+
# reports.
|
102
110
|
#
|
103
111
|
# @param notification [NullNotification]
|
104
112
|
|
@@ -106,9 +114,10 @@ module RSpec
|
|
106
114
|
# @api public
|
107
115
|
# @group Suite Notifications
|
108
116
|
#
|
109
|
-
# This method is invoked after all of the examples have executed. The
|
110
|
-
# to be invoked after this one is {#dump_failures}
|
111
|
-
# (BaseTextFormatter then calls {#
|
117
|
+
# This method is invoked after all of the examples have executed. The
|
118
|
+
# next method to be invoked after this one is {#dump_failures}
|
119
|
+
# (BaseTextFormatter then calls {#dump_failures} once for each failed
|
120
|
+
# example).
|
112
121
|
#
|
113
122
|
# @param notification [NullNotification]
|
114
123
|
|
@@ -124,11 +133,11 @@ module RSpec
|
|
124
133
|
# @api public
|
125
134
|
# @group Suite Notifications
|
126
135
|
#
|
127
|
-
# This method is invoked after the dumping of examples and failures.
|
128
|
-
# is assigned to a corresponding attribute.
|
136
|
+
# This method is invoked after the dumping of examples and failures.
|
137
|
+
# Each parameter is assigned to a corresponding attribute.
|
129
138
|
#
|
130
|
-
# @param summary [SummaryNotification] containing duration,
|
131
|
-
#
|
139
|
+
# @param summary [SummaryNotification] containing duration,
|
140
|
+
# example_count, failure_count and pending_count
|
132
141
|
|
133
142
|
# @method dump_profile
|
134
143
|
# @api public
|
@@ -137,14 +146,14 @@ module RSpec
|
|
137
146
|
# This method is invoked after the dumping the summary if profiling is
|
138
147
|
# enabled.
|
139
148
|
#
|
140
|
-
# @param profile [ProfileNotification] containing duration,
|
141
|
-
#
|
149
|
+
# @param profile [ProfileNotification] containing duration,
|
150
|
+
# slowest_examples and slowest_example_groups
|
142
151
|
|
143
152
|
# @method dump_pending
|
144
153
|
# @api public
|
145
154
|
# @group Suite Notifications
|
146
155
|
#
|
147
|
-
# Outputs a report of pending examples.
|
156
|
+
# Outputs a report of pending examples. This gets invoked
|
148
157
|
# after the summary if option is set to do so.
|
149
158
|
#
|
150
159
|
# @param notification [NullNotification]
|
@@ -3,7 +3,8 @@ module RSpec
|
|
3
3
|
module Formatters
|
4
4
|
# @api private
|
5
5
|
#
|
6
|
-
# Extracts code snippets by looking at the backtrace of the passed error
|
6
|
+
# Extracts code snippets by looking at the backtrace of the passed error
|
7
|
+
# and applies synax highlighting and line numbers using html.
|
7
8
|
class SnippetExtractor
|
8
9
|
# @private
|
9
10
|
class NullConverter
|
@@ -33,7 +34,8 @@ module RSpec
|
|
33
34
|
# Extract lines of code corresponding to a backtrace.
|
34
35
|
#
|
35
36
|
# @param backtrace [String] the backtrace from a test failure
|
36
|
-
# @return [String] highlighted code snippet indicating where the test
|
37
|
+
# @return [String] highlighted code snippet indicating where the test
|
38
|
+
# failure occured
|
37
39
|
#
|
38
40
|
# @see #post_process
|
39
41
|
def snippet(backtrace)
|
@@ -46,7 +48,8 @@ module RSpec
|
|
46
48
|
#
|
47
49
|
# Create a snippet from a line of code.
|
48
50
|
#
|
49
|
-
# @param error_line [String] file name with line number (i.e.
|
51
|
+
# @param error_line [String] file name with line number (i.e.
|
52
|
+
# 'foo_spec.rb:12')
|
50
53
|
# @return [String] lines around the target line within the file
|
51
54
|
#
|
52
55
|
# @see #lines_around
|
@@ -62,11 +65,13 @@ module RSpec
|
|
62
65
|
|
63
66
|
# @api private
|
64
67
|
#
|
65
|
-
# Extract lines of code centered around a particular line within a
|
68
|
+
# Extract lines of code centered around a particular line within a
|
69
|
+
# source file.
|
66
70
|
#
|
67
71
|
# @param file [String] filename
|
68
72
|
# @param line [Fixnum] line number
|
69
|
-
# @return [String] lines around the target line within the file (2 above
|
73
|
+
# @return [String] lines around the target line within the file (2 above
|
74
|
+
# and 1 below).
|
70
75
|
def lines_around(file, line)
|
71
76
|
if File.file?(file)
|
72
77
|
lines = File.read(file).split("\n")
|
@@ -84,9 +89,11 @@ module RSpec
|
|
84
89
|
|
85
90
|
# @api private
|
86
91
|
#
|
87
|
-
# Adds line numbers to all lines and highlights the line where the
|
92
|
+
# Adds line numbers to all lines and highlights the line where the
|
93
|
+
# failure occurred using html `span` tags.
|
88
94
|
#
|
89
|
-
# @param highlighted [String] syntax-highlighted snippet surrounding the
|
95
|
+
# @param highlighted [String] syntax-highlighted snippet surrounding the
|
96
|
+
# offending line of code
|
90
97
|
# @param offending_line [Fixnum] line where failure occured
|
91
98
|
# @return [String] completed snippet
|
92
99
|
def post_process(highlighted, offending_line)
|
data/lib/rspec/core/hooks.rb
CHANGED
@@ -11,18 +11,20 @@ module RSpec
|
|
11
11
|
#
|
12
12
|
# @overload before(&block)
|
13
13
|
# @overload before(scope, &block)
|
14
|
-
# @param scope [Symbol] `:example`, `:context`, or `:suite`
|
14
|
+
# @param scope [Symbol] `:example`, `:context`, or `:suite`
|
15
|
+
# (defaults to `:example`)
|
15
16
|
# @overload before(scope, conditions, &block)
|
16
|
-
# @param scope [Symbol] `:example`, `:context`, or `:suite`
|
17
|
+
# @param scope [Symbol] `:example`, `:context`, or `:suite`
|
18
|
+
# (defaults to `:example`)
|
17
19
|
# @param conditions [Hash]
|
18
20
|
# constrains this hook to examples matching these conditions e.g.
|
19
|
-
# `before(:example, :ui => true) { ... }` will only run with examples
|
20
|
-
# groups declared with `:ui => true`.
|
21
|
+
# `before(:example, :ui => true) { ... }` will only run with examples
|
22
|
+
# or groups declared with `:ui => true`.
|
21
23
|
# @overload before(conditions, &block)
|
22
24
|
# @param conditions [Hash]
|
23
25
|
# constrains this hook to examples matching these conditions e.g.
|
24
|
-
# `before(:example, :ui => true) { ... }` will only run with examples
|
25
|
-
# groups declared with `:ui => true`.
|
26
|
+
# `before(:example, :ui => true) { ... }` will only run with examples
|
27
|
+
# or groups declared with `:ui => true`.
|
26
28
|
#
|
27
29
|
# @see #after
|
28
30
|
# @see #around
|
@@ -32,39 +34,39 @@ module RSpec
|
|
32
34
|
# @see Configuration
|
33
35
|
#
|
34
36
|
# Declare a block of code to be run before each example (using `:example`)
|
35
|
-
# or once before any example (using `:context`). These are usually
|
36
|
-
# directly in the {ExampleGroup} to which they apply, but they
|
37
|
-
# be shared across multiple groups.
|
37
|
+
# or once before any example (using `:context`). These are usually
|
38
|
+
# declared directly in the {ExampleGroup} to which they apply, but they
|
39
|
+
# can also be shared across multiple groups.
|
38
40
|
#
|
39
41
|
# You can also use `before(:suite)` to run a block of code before any
|
40
|
-
# example groups are run. This should be declared in {RSpec.configure}
|
42
|
+
# example groups are run. This should be declared in {RSpec.configure}.
|
41
43
|
#
|
42
|
-
# Instance variables declared in `before(:example)` or `before(:context)`
|
43
|
-
# accessible within each example.
|
44
|
+
# Instance variables declared in `before(:example)` or `before(:context)`
|
45
|
+
# are accessible within each example.
|
44
46
|
#
|
45
47
|
# ### Order
|
46
48
|
#
|
47
49
|
# `before` hooks are stored in three scopes, which are run in order:
|
48
|
-
# `:suite`, `:context`, and `:example`. They can also be declared in
|
49
|
-
# different places: `RSpec.configure`, a parent group, the current
|
50
|
-
# They are run in the following order:
|
51
|
-
#
|
52
|
-
# before(:suite) #
|
53
|
-
# before(:context) #
|
54
|
-
# before(:context) #
|
55
|
-
# before(:context) #
|
56
|
-
# before(:example) #
|
57
|
-
# before(:example) #
|
58
|
-
# before(:example) #
|
50
|
+
# `:suite`, `:context`, and `:example`. They can also be declared in
|
51
|
+
# several different places: `RSpec.configure`, a parent group, the current
|
52
|
+
# group. They are run in the following order:
|
53
|
+
#
|
54
|
+
# before(:suite) # Declared in RSpec.configure.
|
55
|
+
# before(:context) # Declared in RSpec.configure.
|
56
|
+
# before(:context) # Declared in a parent group.
|
57
|
+
# before(:context) # Declared in the current group.
|
58
|
+
# before(:example) # Declared in RSpec.configure.
|
59
|
+
# before(:example) # Declared in a parent group.
|
60
|
+
# before(:example) # Declared in the current group.
|
59
61
|
#
|
60
62
|
# If more than one `before` is declared within any one scope, they are run
|
61
63
|
# in the order in which they are declared.
|
62
64
|
#
|
63
65
|
# ### Conditions
|
64
66
|
#
|
65
|
-
# When you add a conditions hash to `before(:example)` or
|
66
|
-
# RSpec will only apply that hook to groups or
|
67
|
-
# conditions. e.g.
|
67
|
+
# When you add a conditions hash to `before(:example)` or
|
68
|
+
# `before(:context)`, RSpec will only apply that hook to groups or
|
69
|
+
# examples that match the conditions. e.g.
|
68
70
|
#
|
69
71
|
# RSpec.configure do |config|
|
70
72
|
# config.before(:example, :authorized => true) do
|
@@ -73,19 +75,26 @@ module RSpec
|
|
73
75
|
# end
|
74
76
|
#
|
75
77
|
# describe Something, :authorized => true do
|
76
|
-
# #
|
78
|
+
# # The before hook will run in before each example in this group.
|
77
79
|
# end
|
78
80
|
#
|
79
81
|
# describe SomethingElse do
|
80
82
|
# it "does something", :authorized => true do
|
81
|
-
# #
|
83
|
+
# # The before hook will run before this example.
|
82
84
|
# end
|
83
85
|
#
|
84
86
|
# it "does something else" do
|
85
|
-
# #
|
87
|
+
# # The hook will not run before this example.
|
86
88
|
# end
|
87
89
|
# end
|
88
90
|
#
|
91
|
+
# Note that filtered config `:context` hooks can still be applied
|
92
|
+
# to individual examples that have matching metadata. Just like
|
93
|
+
# Ruby's object model is that every object has a singleton class
|
94
|
+
# which has only a single instance, RSpec's model is that every
|
95
|
+
# example has a singleton example group containing just the one
|
96
|
+
# example.
|
97
|
+
#
|
89
98
|
# ### Warning: `before(:suite, :with => :conditions)`
|
90
99
|
#
|
91
100
|
# The conditions hash is used to match against specific examples. Since
|
@@ -113,22 +122,23 @@ module RSpec
|
|
113
122
|
# recommend that you avoid this as there are a number of gotchas, as well
|
114
123
|
# as things that simply don't work.
|
115
124
|
#
|
116
|
-
# ####
|
125
|
+
# #### Context
|
117
126
|
#
|
118
|
-
# `before(:context)` is run in an example that is generated to provide
|
119
|
-
# context for the block.
|
127
|
+
# `before(:context)` is run in an example that is generated to provide
|
128
|
+
# group context for the block.
|
120
129
|
#
|
121
|
-
# ####
|
130
|
+
# #### Instance variables
|
122
131
|
#
|
123
|
-
# Instance variables declared in `before(:context)` are shared across all
|
124
|
-
# examples in the group.
|
132
|
+
# Instance variables declared in `before(:context)` are shared across all
|
133
|
+
# the examples in the group. This means that each example can change the
|
125
134
|
# state of a shared object, resulting in an ordering dependency that can
|
126
135
|
# make it difficult to reason about failures.
|
127
136
|
#
|
128
|
-
# ####
|
137
|
+
# #### Unsupported RSpec constructs
|
129
138
|
#
|
130
139
|
# RSpec has several constructs that reset state between each example
|
131
|
-
# automatically. These are not intended for use from within
|
140
|
+
# automatically. These are not intended for use from within
|
141
|
+
# `before(:context)`:
|
132
142
|
#
|
133
143
|
# * `let` declarations
|
134
144
|
# * `subject` declarations
|
@@ -138,13 +148,13 @@ module RSpec
|
|
138
148
|
#
|
139
149
|
# Mock object frameworks and database transaction managers (like
|
140
150
|
# ActiveRecord) are typically designed around the idea of setting up
|
141
|
-
# before an example, running that one example, and then tearing down.
|
142
|
-
#
|
143
|
-
# `before(:context)`, but get torn down before the first real example is
|
144
|
-
# run.
|
151
|
+
# before an example, running that one example, and then tearing down. This
|
152
|
+
# means that mocks and stubs can (sometimes) be declared in
|
153
|
+
# `before(:context)`, but get torn down before the first real example is
|
154
|
+
# ever run.
|
145
155
|
#
|
146
|
-
# You _can_ create database-backed model objects in a `before(:context)`
|
147
|
-
# rspec-rails, but it will not be wrapped in a transaction for you, so
|
156
|
+
# You _can_ create database-backed model objects in a `before(:context)`
|
157
|
+
# in rspec-rails, but it will not be wrapped in a transaction for you, so
|
148
158
|
# you are on your own to clean up in an `after(:context)` block.
|
149
159
|
#
|
150
160
|
# @example before(:example) declared in an {ExampleGroup}
|
@@ -155,7 +165,7 @@ module RSpec
|
|
155
165
|
# end
|
156
166
|
#
|
157
167
|
# it "does something" do
|
158
|
-
# #
|
168
|
+
# # Here you can access @thing.
|
159
169
|
# end
|
160
170
|
# end
|
161
171
|
#
|
@@ -181,6 +191,9 @@ module RSpec
|
|
181
191
|
#
|
182
192
|
# @note The `:example` and `:context` scopes are also available as
|
183
193
|
# `:each` and `:all`, respectively. Use whichever you prefer.
|
194
|
+
# @note The `:scope` alias is only supported for hooks registered on
|
195
|
+
# `RSpec.configuration` since they exist independently of any
|
196
|
+
# example or example group.
|
184
197
|
def before(*args, &block)
|
185
198
|
hooks.register :append, :before, *args, &block
|
186
199
|
end
|
@@ -198,18 +211,20 @@ module RSpec
|
|
198
211
|
# @api public
|
199
212
|
# @overload after(&block)
|
200
213
|
# @overload after(scope, &block)
|
201
|
-
# @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
|
214
|
+
# @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
|
215
|
+
# `:example`)
|
202
216
|
# @overload after(scope, conditions, &block)
|
203
|
-
# @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
|
217
|
+
# @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
|
218
|
+
# `:example`)
|
204
219
|
# @param conditions [Hash]
|
205
220
|
# constrains this hook to examples matching these conditions e.g.
|
206
|
-
# `after(:example, :ui => true) { ... }` will only run with examples
|
207
|
-
# groups declared with `:ui => true`.
|
221
|
+
# `after(:example, :ui => true) { ... }` will only run with examples
|
222
|
+
# or groups declared with `:ui => true`.
|
208
223
|
# @overload after(conditions, &block)
|
209
224
|
# @param conditions [Hash]
|
210
225
|
# constrains this hook to examples matching these conditions e.g.
|
211
|
-
# `after(:example, :ui => true) { ... }` will only run with examples
|
212
|
-
# groups declared with `:ui => true`.
|
226
|
+
# `after(:example, :ui => true) { ... }` will only run with examples
|
227
|
+
# or groups declared with `:ui => true`.
|
213
228
|
#
|
214
229
|
# @see #before
|
215
230
|
# @see #around
|
@@ -218,31 +233,31 @@ module RSpec
|
|
218
233
|
# @see SharedExampleGroup
|
219
234
|
# @see Configuration
|
220
235
|
#
|
221
|
-
# Declare a block of code to be run after each example (using `:example`)
|
222
|
-
# once after all examples n the context (using `:context`). See
|
223
|
-
# more information about ordering.
|
236
|
+
# Declare a block of code to be run after each example (using `:example`)
|
237
|
+
# or once after all examples n the context (using `:context`). See
|
238
|
+
# {#before} for more information about ordering.
|
224
239
|
#
|
225
240
|
# ### Exceptions
|
226
241
|
#
|
227
242
|
# `after` hooks are guaranteed to run even when there are exceptions in
|
228
|
-
# `before` hooks or examples.
|
243
|
+
# `before` hooks or examples. When an exception is raised in an after
|
229
244
|
# block, the exception is captured for later reporting, and subsequent
|
230
245
|
# `after` blocks are run.
|
231
246
|
#
|
232
247
|
# ### Order
|
233
248
|
#
|
234
249
|
# `after` hooks are stored in three scopes, which are run in order:
|
235
|
-
# `:example`, `:context`, and `:suite`. They can also be declared in
|
236
|
-
# different places: `RSpec.configure`, a parent group, the current
|
237
|
-
# They are run in the following order:
|
238
|
-
#
|
239
|
-
# after(:example) #
|
240
|
-
# after(:example) #
|
241
|
-
# after(:example) #
|
242
|
-
# after(:context) #
|
243
|
-
# after(:context) #
|
244
|
-
# after(:context) #
|
245
|
-
# after(:suite) #
|
250
|
+
# `:example`, `:context`, and `:suite`. They can also be declared in
|
251
|
+
# several different places: `RSpec.configure`, a parent group, the current
|
252
|
+
# group. They are run in the following order:
|
253
|
+
#
|
254
|
+
# after(:example) # Declared in the current group.
|
255
|
+
# after(:example) # Declared in a parent group.
|
256
|
+
# after(:example) # Declared in RSpec.configure.
|
257
|
+
# after(:context) # Declared in the current group.
|
258
|
+
# after(:context) # Declared in a parent group.
|
259
|
+
# after(:context) # Declared in RSpec.configure.
|
260
|
+
# after(:suite) # Declared in RSpec.configure.
|
246
261
|
#
|
247
262
|
# This is the reverse of the order in which `before` hooks are run.
|
248
263
|
# Similarly, if more than one `after` is declared within any one scope,
|
@@ -250,6 +265,9 @@ module RSpec
|
|
250
265
|
#
|
251
266
|
# @note The `:example` and `:context` scopes are also available as
|
252
267
|
# `:each` and `:all`, respectively. Use whichever you prefer.
|
268
|
+
# @note The `:scope` alias is only supported for hooks registered on
|
269
|
+
# `RSpec.configuration` since they exist independently of any
|
270
|
+
# example or example group.
|
253
271
|
def after(*args, &block)
|
254
272
|
hooks.register :prepend, :after, *args, &block
|
255
273
|
end
|
@@ -274,15 +292,13 @@ module RSpec
|
|
274
292
|
# @param scope [Symbol] `:example` (defaults to `:example`)
|
275
293
|
# present for syntax parity with `before` and `after`, but
|
276
294
|
# `:example`/`:each` is the only supported value.
|
277
|
-
# @param conditions [Hash]
|
278
|
-
#
|
279
|
-
#
|
280
|
-
# groups declared with `:ui => true`.
|
295
|
+
# @param conditions [Hash] constrains this hook to examples matching
|
296
|
+
# these conditions e.g. `around(:example, :ui => true) { ... }` will
|
297
|
+
# only run with examples or groups declared with `:ui => true`.
|
281
298
|
# @overload around(conditions, &block)
|
282
|
-
# @param conditions [Hash]
|
283
|
-
#
|
284
|
-
#
|
285
|
-
# groups declared with `:ui => true`.
|
299
|
+
# @param conditions [Hash] constrains this hook to examples matching
|
300
|
+
# these conditions e.g. `around(:example, :ui => true) { ... }` will
|
301
|
+
# only run with examples or groups declared with `:ui => true`.
|
286
302
|
#
|
287
303
|
# @yield [Example] the example to run
|
288
304
|
#
|
@@ -300,13 +316,13 @@ module RSpec
|
|
300
316
|
# after the example. It is your responsibility to run the example:
|
301
317
|
#
|
302
318
|
# around(:example) do |ex|
|
303
|
-
# #
|
319
|
+
# # Do some stuff before.
|
304
320
|
# ex.run
|
305
|
-
# #
|
321
|
+
# # Do some stuff after.
|
306
322
|
# end
|
307
323
|
#
|
308
324
|
# The yielded example aliases `run` with `call`, which lets you treat it
|
309
|
-
# like a `Proc`.
|
325
|
+
# like a `Proc`. This is especially handy when working with libaries
|
310
326
|
# that manage their own setup and teardown using a block or proc syntax,
|
311
327
|
# e.g.
|
312
328
|
#
|
@@ -320,12 +336,7 @@ module RSpec
|
|
320
336
|
# @private
|
321
337
|
# Holds the various registered hooks.
|
322
338
|
def hooks
|
323
|
-
@hooks ||= HookCollections.new(
|
324
|
-
self,
|
325
|
-
:around => { :example => AroundHookCollection.new },
|
326
|
-
:before => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new },
|
327
|
-
:after => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new }
|
328
|
-
)
|
339
|
+
@hooks ||= HookCollections.new(self, FilterableItemRepository::UpdateOptimized)
|
329
340
|
end
|
330
341
|
|
331
342
|
private
|
@@ -338,10 +349,6 @@ module RSpec
|
|
338
349
|
@block = block
|
339
350
|
@options = options
|
340
351
|
end
|
341
|
-
|
342
|
-
def options_apply?(example_or_group)
|
343
|
-
example_or_group.all_apply?(options)
|
344
|
-
end
|
345
352
|
end
|
346
353
|
|
347
354
|
# @private
|
@@ -363,7 +370,7 @@ module RSpec
|
|
363
370
|
def run(example)
|
364
371
|
example.instance_exec(example, &block)
|
365
372
|
rescue Exception => e
|
366
|
-
# TODO:
|
373
|
+
# TODO: Come up with a better solution for this.
|
367
374
|
RSpec.configuration.reporter.message <<-EOS
|
368
375
|
|
369
376
|
An error occurred in an `after(:context)` hook.
|
@@ -379,7 +386,8 @@ EOS
|
|
379
386
|
def execute_with(example, procsy)
|
380
387
|
example.instance_exec(procsy, &block)
|
381
388
|
return if procsy.executed?
|
382
|
-
Pending.mark_skipped!(example,
|
389
|
+
Pending.mark_skipped!(example,
|
390
|
+
"#{hook_description} did not execute the example")
|
383
391
|
end
|
384
392
|
|
385
393
|
if Proc.method_defined?(:source_location)
|
@@ -394,113 +402,81 @@ EOS
|
|
394
402
|
end
|
395
403
|
|
396
404
|
# @private
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
def initialize(
|
409
|
-
@
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
self.class.
|
417
|
-
new(hooks.select { |hook| hook.options_apply?(example_or_group) }).
|
418
|
-
with(example_or_group)
|
419
|
-
end
|
420
|
-
|
421
|
-
def with(example)
|
422
|
-
@example = example
|
423
|
-
self
|
424
|
-
end
|
425
|
-
|
426
|
-
def run
|
427
|
-
hooks.each { |h| h.run(@example) }
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
# @private
|
432
|
-
class AroundHookCollection < BaseHookCollection
|
433
|
-
def for(example, initial_procsy=nil)
|
434
|
-
self.class.new(hooks.select { |hook| hook.options_apply?(example) }).
|
435
|
-
with(example, initial_procsy)
|
436
|
-
end
|
437
|
-
|
438
|
-
def with(example, initial_procsy)
|
439
|
-
@example = example
|
440
|
-
@initial_procsy = initial_procsy
|
441
|
-
self
|
405
|
+
#
|
406
|
+
# This provides the primary API used by other parts of rspec-core. By hiding all
|
407
|
+
# implementation details behind this facade, it's allowed us to heavily optimize
|
408
|
+
# this, so that, for example, hook collection objects are only instantiated when
|
409
|
+
# a hook is added. This allows us to avoid many object allocations for the common
|
410
|
+
# case of a group having no hooks.
|
411
|
+
#
|
412
|
+
# This is only possible because this interface provides a "tell, don't ask"-style
|
413
|
+
# API, so that callers _tell_ this class what to do with the hooks, rather than
|
414
|
+
# asking this class for a list of hooks, and then doing something with them.
|
415
|
+
class HookCollections
|
416
|
+
def initialize(owner, filterable_item_repo_class)
|
417
|
+
@owner = owner
|
418
|
+
@filterable_item_repo_class = filterable_item_repo_class
|
419
|
+
@before_example_hooks = nil
|
420
|
+
@after_example_hooks = nil
|
421
|
+
@before_context_hooks = nil
|
422
|
+
@after_context_hooks = nil
|
423
|
+
@around_example_hooks = nil
|
442
424
|
end
|
443
425
|
|
444
|
-
def
|
445
|
-
|
446
|
-
procsy.wrap { around_hook.execute_with(@example, procsy) }
|
447
|
-
end.call
|
448
|
-
end
|
449
|
-
end
|
426
|
+
def register_globals(host, globals)
|
427
|
+
parent_groups = host.parent_groups
|
450
428
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
@group = group
|
455
|
-
self
|
456
|
-
end
|
429
|
+
process(host, parent_groups, globals, :before, :example, &:options)
|
430
|
+
process(host, parent_groups, globals, :after, :example, &:options)
|
431
|
+
process(host, parent_groups, globals, :around, :example, &:options)
|
457
432
|
|
458
|
-
|
459
|
-
|
433
|
+
process(host, parent_groups, globals, :before, :context, &:options)
|
434
|
+
process(host, parent_groups, globals, :after, :context, &:options)
|
460
435
|
end
|
461
|
-
end
|
462
436
|
|
463
|
-
|
464
|
-
|
465
|
-
def initialize(owner, data)
|
466
|
-
@owner = owner
|
467
|
-
@data = data
|
468
|
-
end
|
437
|
+
def register_global_singleton_context_hooks(example, globals)
|
438
|
+
parent_groups = example.example_group.parent_groups
|
469
439
|
|
470
|
-
|
471
|
-
|
440
|
+
process(example, parent_groups, globals, :before, :context) { {} }
|
441
|
+
process(example, parent_groups, globals, :after, :context) { {} }
|
472
442
|
end
|
473
443
|
|
474
|
-
def
|
475
|
-
|
476
|
-
process(host, globals, :after, :example)
|
477
|
-
process(host, globals, :around, :example)
|
478
|
-
|
479
|
-
process(host, globals, :before, :context)
|
480
|
-
process(host, globals, :after, :context)
|
481
|
-
end
|
444
|
+
def register(prepend_or_append, position, *args, &block)
|
445
|
+
scope, options = scope_and_options_from(*args)
|
482
446
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
447
|
+
if scope == :suite
|
448
|
+
# TODO: consider making this an error in RSpec 4. For SemVer reasons,
|
449
|
+
# we are only warning in RSpec 3.
|
450
|
+
RSpec.warn_with "WARNING: `#{position}(:suite)` hooks are only supported on " \
|
451
|
+
"the RSpec configuration object. This " \
|
452
|
+
"`#{position}(:suite)` hook, registered on an example " \
|
453
|
+
"group, will be ignored."
|
454
|
+
return
|
455
|
+
end
|
488
456
|
|
489
|
-
|
490
|
-
scope, options
|
491
|
-
self[hook][scope].__send__(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
|
457
|
+
hook = HOOK_TYPES[position][scope].new(block, options)
|
458
|
+
ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook, options)
|
492
459
|
end
|
493
460
|
|
494
461
|
# @private
|
495
462
|
#
|
496
463
|
# Runs all of the blocks stored with the hook in the context of the
|
497
464
|
# example. If no example is provided, just calls the hook directly.
|
498
|
-
def run(
|
465
|
+
def run(position, scope, example_or_group)
|
499
466
|
return if RSpec.configuration.dry_run?
|
500
|
-
|
467
|
+
|
468
|
+
if scope == :context
|
469
|
+
run_owned_hooks_for(position, :context, example_or_group)
|
470
|
+
else
|
471
|
+
case position
|
472
|
+
when :before then run_example_hooks_for(example_or_group, :before, :reverse_each)
|
473
|
+
when :after then run_example_hooks_for(example_or_group, :after, :each)
|
474
|
+
when :around then run_around_example_hooks_for(example_or_group) { yield }
|
475
|
+
end
|
476
|
+
end
|
501
477
|
end
|
502
478
|
|
503
|
-
SCOPES = [:example, :context
|
479
|
+
SCOPES = [:example, :context]
|
504
480
|
|
505
481
|
SCOPE_ALIASES = { :each => :example, :all => :context }
|
506
482
|
|
@@ -512,17 +488,89 @@ EOS
|
|
512
488
|
|
513
489
|
HOOK_TYPES[:after][:context] = AfterContextHook
|
514
490
|
|
491
|
+
protected
|
492
|
+
|
493
|
+
EMPTY_HOOK_ARRAY = [].freeze
|
494
|
+
|
495
|
+
def matching_hooks_for(position, scope, example_or_group)
|
496
|
+
repository = hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }
|
497
|
+
|
498
|
+
# It would be nice to not have to switch on type here, but
|
499
|
+
# we don't want to define `ExampleGroup#metadata` because then
|
500
|
+
# `metadata` from within an individual example would return the
|
501
|
+
# group's metadata but the user would probably expect it to be
|
502
|
+
# the example's metadata.
|
503
|
+
metadata = case example_or_group
|
504
|
+
when ExampleGroup then example_or_group.class.metadata
|
505
|
+
else example_or_group.metadata
|
506
|
+
end
|
507
|
+
|
508
|
+
repository.items_for(metadata)
|
509
|
+
end
|
510
|
+
|
511
|
+
def all_hooks_for(position, scope)
|
512
|
+
hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.items_and_filters.map(&:first)
|
513
|
+
end
|
514
|
+
|
515
|
+
def run_owned_hooks_for(position, scope, example_or_group)
|
516
|
+
matching_hooks_for(position, scope, example_or_group).each do |hook|
|
517
|
+
hook.run(example_or_group)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def processable_hooks_for(position, scope, host)
|
522
|
+
if scope == :example
|
523
|
+
all_hooks_for(position, scope)
|
524
|
+
else
|
525
|
+
matching_hooks_for(position, scope, host)
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
515
529
|
private
|
516
530
|
|
517
|
-
def
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
531
|
+
def hooks_for(position, scope)
|
532
|
+
if position == :before
|
533
|
+
scope == :example ? @before_example_hooks : @before_context_hooks
|
534
|
+
elsif position == :after
|
535
|
+
scope == :example ? @after_example_hooks : @after_context_hooks
|
536
|
+
else # around
|
537
|
+
@around_example_hooks
|
538
|
+
end || yield
|
539
|
+
end
|
540
|
+
|
541
|
+
def ensure_hooks_initialized_for(position, scope)
|
542
|
+
if position == :before
|
543
|
+
if scope == :example
|
544
|
+
@before_example_hooks ||= @filterable_item_repo_class.new(:all?)
|
545
|
+
else
|
546
|
+
@before_context_hooks ||= @filterable_item_repo_class.new(:all?)
|
547
|
+
end
|
548
|
+
elsif position == :after
|
549
|
+
if scope == :example
|
550
|
+
@after_example_hooks ||= @filterable_item_repo_class.new(:all?)
|
551
|
+
else
|
552
|
+
@after_context_hooks ||= @filterable_item_repo_class.new(:all?)
|
553
|
+
end
|
554
|
+
else # around
|
555
|
+
@around_example_hooks ||= @filterable_item_repo_class.new(:all?)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def process(host, parent_groups, globals, position, scope)
|
560
|
+
hooks_to_process = globals.processable_hooks_for(position, scope, host)
|
561
|
+
return if hooks_to_process.empty?
|
562
|
+
|
563
|
+
hooks_to_process -= FlatMap.flat_map(parent_groups) do |group|
|
564
|
+
group.hooks.all_hooks_for(position, scope)
|
522
565
|
end
|
566
|
+
return if hooks_to_process.empty?
|
567
|
+
|
568
|
+
repository = ensure_hooks_initialized_for(position, scope)
|
569
|
+
hooks_to_process.each { |hook| repository.append hook, (yield hook) }
|
523
570
|
end
|
524
571
|
|
525
572
|
def scope_and_options_from(*args)
|
573
|
+
return :suite if args.first == :suite
|
526
574
|
scope = extract_scope_from(args)
|
527
575
|
meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
|
528
576
|
return scope, meta
|
@@ -532,58 +580,51 @@ EOS
|
|
532
580
|
if known_scope?(args.first)
|
533
581
|
normalized_scope_for(args.shift)
|
534
582
|
elsif args.any? { |a| a.is_a?(Symbol) }
|
535
|
-
error_message = "You must explicitly give a scope
|
583
|
+
error_message = "You must explicitly give a scope " \
|
584
|
+
"(#{SCOPES.join(", ")}) or scope alias " \
|
585
|
+
"(#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as " \
|
586
|
+
"metadata for a hook."
|
536
587
|
raise ArgumentError.new error_message
|
537
588
|
else
|
538
589
|
:example
|
539
590
|
end
|
540
591
|
end
|
541
592
|
|
542
|
-
# @api private
|
543
593
|
def known_scope?(scope)
|
544
594
|
SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope)
|
545
595
|
end
|
546
596
|
|
547
|
-
# @api private
|
548
597
|
def normalized_scope_for(scope)
|
549
598
|
SCOPE_ALIASES[scope] || scope
|
550
599
|
end
|
551
600
|
|
552
|
-
def
|
553
|
-
|
554
|
-
|
555
|
-
before_context_hooks_for(example_or_group)
|
556
|
-
when [:after, :context]
|
557
|
-
after_context_hooks_for(example_or_group)
|
558
|
-
when [:around, :example]
|
559
|
-
around_example_hooks_for(example_or_group, initial_procsy)
|
560
|
-
when [:before, :example]
|
561
|
-
before_example_hooks_for(example_or_group)
|
562
|
-
when [:after, :example]
|
563
|
-
after_example_hooks_for(example_or_group)
|
564
|
-
when [:before, :suite], [:after, :suite]
|
565
|
-
self[hook][:suite].with(example_or_group)
|
601
|
+
def run_example_hooks_for(example, position, each_method)
|
602
|
+
owner_parent_groups.__send__(each_method) do |group|
|
603
|
+
group.hooks.run_owned_hooks_for(position, :example, example)
|
566
604
|
end
|
567
605
|
end
|
568
606
|
|
569
|
-
def
|
570
|
-
|
571
|
-
|
607
|
+
def run_around_example_hooks_for(example)
|
608
|
+
hooks = FlatMap.flat_map(owner_parent_groups) do |group|
|
609
|
+
group.hooks.matching_hooks_for(:around, :example, example)
|
610
|
+
end
|
572
611
|
|
573
|
-
|
574
|
-
GroupHookCollection.new(self[:after][:context]).for(group)
|
575
|
-
end
|
612
|
+
return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy`
|
576
613
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
end
|
614
|
+
initial_procsy = Example::Procsy.new(example) { yield }
|
615
|
+
hooks.inject(initial_procsy) do |procsy, around_hook|
|
616
|
+
procsy.wrap { around_hook.execute_with(example, procsy) }
|
617
|
+
end.call
|
581
618
|
end
|
582
619
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
end
|
620
|
+
if respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class)
|
621
|
+
def owner_parent_groups
|
622
|
+
@owner.parent_groups
|
623
|
+
end
|
624
|
+
else # Ruby < 2.1 (see https://bugs.ruby-lang.org/issues/8035)
|
625
|
+
def owner_parent_groups
|
626
|
+
@owner_parent_groups ||= [@owner] + @owner.parent_groups
|
627
|
+
end
|
587
628
|
end
|
588
629
|
end
|
589
630
|
end
|