rspec-core 3.1.7 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|