opal-rspec 0.4.0.beta3 → 0.4.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.gitmodules +15 -0
  4. data/.travis.yml +12 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +3 -1
  7. data/Gemfile +6 -7
  8. data/README.md +2 -0
  9. data/Rakefile +12 -50
  10. data/lib/opal/rspec/version.rb +1 -1
  11. data/lib/opal/rspec.rb +14 -0
  12. data/opal/opal/rspec/async.rb +146 -11
  13. data/opal/opal/rspec/fixes.rb +18 -8
  14. data/opal/opal/rspec/requires.rb +45 -0
  15. data/opal/opal/rspec.rb +1 -24
  16. data/opal-rspec.gemspec +1 -1
  17. data/spec/async_spec.rb +4 -5
  18. data/spec/matchers_spec.rb +20 -0
  19. data/spec/named_subject_spec.rb +11 -0
  20. data/spec/should_syntax_spec.rb +17 -0
  21. data/vendor_lib/rspec/autorun.rb +2 -0
  22. data/vendor_lib/rspec/core/backport_random.rb +302 -0
  23. data/vendor_lib/rspec/core/backtrace_formatter.rb +65 -0
  24. data/vendor_lib/rspec/core/command_line.rb +36 -0
  25. data/vendor_lib/rspec/core/configuration.rb +1129 -0
  26. data/vendor_lib/rspec/core/configuration_options.rb +143 -0
  27. data/vendor_lib/rspec/core/drb_command_line.rb +26 -0
  28. data/vendor_lib/rspec/core/drb_options.rb +87 -0
  29. data/vendor_lib/rspec/core/dsl.rb +26 -0
  30. data/vendor_lib/rspec/core/example.rb +312 -0
  31. data/vendor_lib/rspec/core/example_group.rb +540 -0
  32. data/vendor_lib/rspec/core/filter_manager.rb +224 -0
  33. data/vendor_lib/rspec/core/flat_map.rb +17 -0
  34. data/vendor_lib/rspec/core/formatters/base_formatter.rb +291 -0
  35. data/vendor_lib/rspec/core/formatters/base_text_formatter.rb +307 -0
  36. data/vendor_lib/rspec/core/formatters/deprecation_formatter.rb +193 -0
  37. data/vendor_lib/rspec/core/formatters/documentation_formatter.rb +67 -0
  38. data/vendor_lib/rspec/core/formatters/helpers.rb +82 -0
  39. data/vendor_lib/rspec/core/formatters/html_formatter.rb +155 -0
  40. data/vendor_lib/rspec/core/formatters/html_printer.rb +408 -0
  41. data/vendor_lib/rspec/core/formatters/json_formatter.rb +99 -0
  42. data/vendor_lib/rspec/core/formatters/progress_formatter.rb +32 -0
  43. data/vendor_lib/rspec/core/formatters/snippet_extractor.rb +101 -0
  44. data/vendor_lib/rspec/core/formatters.rb +54 -0
  45. data/vendor_lib/rspec/core/hooks.rb +535 -0
  46. data/vendor_lib/rspec/core/memoized_helpers.rb +431 -0
  47. data/vendor_lib/rspec/core/metadata.rb +313 -0
  48. data/vendor_lib/rspec/core/mocking/with_absolutely_nothing.rb +11 -0
  49. data/vendor_lib/rspec/core/mocking/with_flexmock.rb +27 -0
  50. data/vendor_lib/rspec/core/mocking/with_mocha.rb +52 -0
  51. data/vendor_lib/rspec/core/mocking/with_rr.rb +27 -0
  52. data/vendor_lib/rspec/core/mocking/with_rspec.rb +27 -0
  53. data/vendor_lib/rspec/core/option_parser.rb +234 -0
  54. data/vendor_lib/rspec/core/ordering.rb +154 -0
  55. data/vendor_lib/rspec/core/pending.rb +110 -0
  56. data/vendor_lib/rspec/core/project_initializer.rb +88 -0
  57. data/vendor_lib/rspec/core/rake_task.rb +128 -0
  58. data/vendor_lib/rspec/core/reporter.rb +132 -0
  59. data/vendor_lib/rspec/core/ruby_project.rb +44 -0
  60. data/vendor_lib/rspec/core/runner.rb +97 -0
  61. data/vendor_lib/rspec/core/shared_context.rb +53 -0
  62. data/vendor_lib/rspec/core/shared_example_group/collection.rb +27 -0
  63. data/vendor_lib/rspec/core/shared_example_group.rb +146 -0
  64. data/vendor_lib/rspec/core/version.rb +7 -0
  65. data/vendor_lib/rspec/core/warnings.rb +22 -0
  66. data/vendor_lib/rspec/core/world.rb +131 -0
  67. data/vendor_lib/rspec/core.rb +203 -0
  68. data/vendor_lib/rspec/expectations/differ.rb +154 -0
  69. data/vendor_lib/rspec/expectations/errors.rb +9 -0
  70. data/vendor_lib/rspec/expectations/expectation_target.rb +87 -0
  71. data/vendor_lib/rspec/expectations/extensions/object.rb +29 -0
  72. data/vendor_lib/rspec/expectations/extensions.rb +1 -0
  73. data/vendor_lib/rspec/expectations/fail_with.rb +79 -0
  74. data/vendor_lib/rspec/expectations/handler.rb +68 -0
  75. data/vendor_lib/rspec/expectations/syntax.rb +182 -0
  76. data/vendor_lib/rspec/expectations/version.rb +8 -0
  77. data/vendor_lib/rspec/expectations.rb +75 -0
  78. data/vendor_lib/rspec/matchers/built_in/base_matcher.rb +68 -0
  79. data/vendor_lib/rspec/matchers/built_in/be.rb +213 -0
  80. data/vendor_lib/rspec/matchers/built_in/be_instance_of.rb +15 -0
  81. data/vendor_lib/rspec/matchers/built_in/be_kind_of.rb +11 -0
  82. data/vendor_lib/rspec/matchers/built_in/be_within.rb +55 -0
  83. data/vendor_lib/rspec/matchers/built_in/change.rb +141 -0
  84. data/vendor_lib/rspec/matchers/built_in/cover.rb +21 -0
  85. data/vendor_lib/rspec/matchers/built_in/eq.rb +22 -0
  86. data/vendor_lib/rspec/matchers/built_in/eql.rb +23 -0
  87. data/vendor_lib/rspec/matchers/built_in/equal.rb +48 -0
  88. data/vendor_lib/rspec/matchers/built_in/exist.rb +26 -0
  89. data/vendor_lib/rspec/matchers/built_in/has.rb +48 -0
  90. data/vendor_lib/rspec/matchers/built_in/include.rb +61 -0
  91. data/vendor_lib/rspec/matchers/built_in/match.rb +17 -0
  92. data/vendor_lib/rspec/matchers/built_in/match_array.rb +51 -0
  93. data/vendor_lib/rspec/matchers/built_in/raise_error.rb +154 -0
  94. data/vendor_lib/rspec/matchers/built_in/respond_to.rb +74 -0
  95. data/vendor_lib/rspec/matchers/built_in/satisfy.rb +30 -0
  96. data/vendor_lib/rspec/matchers/built_in/start_and_end_with.rb +48 -0
  97. data/vendor_lib/rspec/matchers/built_in/throw_symbol.rb +94 -0
  98. data/vendor_lib/rspec/matchers/built_in/yield.rb +297 -0
  99. data/vendor_lib/rspec/matchers/built_in.rb +39 -0
  100. data/vendor_lib/rspec/matchers/compatibility.rb +14 -0
  101. data/vendor_lib/rspec/matchers/configuration.rb +113 -0
  102. data/vendor_lib/rspec/matchers/dsl.rb +23 -0
  103. data/vendor_lib/rspec/matchers/generated_descriptions.rb +35 -0
  104. data/vendor_lib/rspec/matchers/matcher.rb +301 -0
  105. data/vendor_lib/rspec/matchers/method_missing.rb +12 -0
  106. data/vendor_lib/rspec/matchers/operator_matcher.rb +99 -0
  107. data/vendor_lib/rspec/matchers/pretty.rb +70 -0
  108. data/vendor_lib/rspec/matchers/test_unit_integration.rb +11 -0
  109. data/vendor_lib/rspec/matchers.rb +633 -0
  110. data/vendor_lib/rspec/mocks/any_instance/chain.rb +92 -0
  111. data/vendor_lib/rspec/mocks/any_instance/expectation_chain.rb +47 -0
  112. data/vendor_lib/rspec/mocks/any_instance/message_chains.rb +75 -0
  113. data/vendor_lib/rspec/mocks/any_instance/recorder.rb +200 -0
  114. data/vendor_lib/rspec/mocks/any_instance/stub_chain.rb +45 -0
  115. data/vendor_lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  116. data/vendor_lib/rspec/mocks/argument_list_matcher.rb +104 -0
  117. data/vendor_lib/rspec/mocks/argument_matchers.rb +264 -0
  118. data/vendor_lib/rspec/mocks/arity_calculator.rb +66 -0
  119. data/vendor_lib/rspec/mocks/configuration.rb +111 -0
  120. data/vendor_lib/rspec/mocks/error_generator.rb +203 -0
  121. data/vendor_lib/rspec/mocks/errors.rb +12 -0
  122. data/vendor_lib/rspec/mocks/example_methods.rb +201 -0
  123. data/vendor_lib/rspec/mocks/extensions/marshal.rb +17 -0
  124. data/vendor_lib/rspec/mocks/framework.rb +36 -0
  125. data/vendor_lib/rspec/mocks/instance_method_stasher.rb +112 -0
  126. data/vendor_lib/rspec/mocks/matchers/have_received.rb +99 -0
  127. data/vendor_lib/rspec/mocks/matchers/receive.rb +112 -0
  128. data/vendor_lib/rspec/mocks/matchers/receive_messages.rb +72 -0
  129. data/vendor_lib/rspec/mocks/message_expectation.rb +643 -0
  130. data/vendor_lib/rspec/mocks/method_double.rb +209 -0
  131. data/vendor_lib/rspec/mocks/method_reference.rb +95 -0
  132. data/vendor_lib/rspec/mocks/mock.rb +7 -0
  133. data/vendor_lib/rspec/mocks/mutate_const.rb +406 -0
  134. data/vendor_lib/rspec/mocks/object_reference.rb +90 -0
  135. data/vendor_lib/rspec/mocks/order_group.rb +82 -0
  136. data/vendor_lib/rspec/mocks/proxy.rb +269 -0
  137. data/vendor_lib/rspec/mocks/proxy_for_nil.rb +37 -0
  138. data/vendor_lib/rspec/mocks/space.rb +95 -0
  139. data/vendor_lib/rspec/mocks/standalone.rb +3 -0
  140. data/vendor_lib/rspec/mocks/stub_chain.rb +51 -0
  141. data/vendor_lib/rspec/mocks/syntax.rb +374 -0
  142. data/vendor_lib/rspec/mocks/targets.rb +90 -0
  143. data/vendor_lib/rspec/mocks/test_double.rb +109 -0
  144. data/vendor_lib/rspec/mocks/verifying_double.rb +77 -0
  145. data/vendor_lib/rspec/mocks/verifying_message_expecation.rb +60 -0
  146. data/vendor_lib/rspec/mocks/verifying_proxy.rb +151 -0
  147. data/vendor_lib/rspec/mocks/version.rb +7 -0
  148. data/vendor_lib/rspec/mocks.rb +100 -0
  149. data/vendor_lib/rspec/support/caller_filter.rb +56 -0
  150. data/vendor_lib/rspec/support/spec/deprecation_helpers.rb +29 -0
  151. data/vendor_lib/rspec/support/spec/in_sub_process.rb +40 -0
  152. data/vendor_lib/rspec/support/spec/stderr_splitter.rb +50 -0
  153. data/vendor_lib/rspec/support/spec.rb +14 -0
  154. data/vendor_lib/rspec/support/version.rb +7 -0
  155. data/vendor_lib/rspec/support/warnings.rb +41 -0
  156. data/vendor_lib/rspec/support.rb +6 -0
  157. data/vendor_lib/rspec/version.rb +5 -0
  158. data/vendor_lib/rspec-expectations.rb +1 -0
  159. data/vendor_lib/rspec.rb +3 -0
  160. metadata +163 -4
  161. data/opal/opal/rspec/rspec.js +0 -20384
@@ -0,0 +1,101 @@
1
+ module RSpec
2
+ module Core
3
+ module Formatters
4
+ # @api private
5
+ #
6
+ # Extracts code snippets by looking at the backtrace of the passed error and applies synax highlighting and line numbers using html.
7
+ class SnippetExtractor
8
+ class NullConverter
9
+ def convert(code)
10
+ %Q(#{code}\n<span class="comment"># Install the coderay gem to get syntax highlighting</span>)
11
+ end
12
+ end
13
+
14
+ class CoderayConverter
15
+ def convert(code)
16
+ CodeRay.scan(code, :ruby).html(:line_numbers => false)
17
+ end
18
+ end
19
+
20
+ begin
21
+ require 'coderay'
22
+ @@converter = CoderayConverter.new
23
+ rescue LoadError
24
+ @@converter = NullConverter.new
25
+ end
26
+
27
+ # @api private
28
+ #
29
+ # Extract lines of code corresponding to a backtrace.
30
+ #
31
+ # @param [String] backtrace the backtrace from a test failure
32
+ # @return [String] highlighted code snippet indicating where the test failure occured
33
+ #
34
+ # @see #post_process
35
+ def snippet(backtrace)
36
+ raw_code, line = snippet_for(backtrace[0])
37
+ highlighted = @@converter.convert(raw_code)
38
+ post_process(highlighted, line)
39
+ end
40
+
41
+ # @api private
42
+ #
43
+ # Create a snippet from a line of code.
44
+ #
45
+ # @param [String] error_line file name with line number (i.e. 'foo_spec.rb:12')
46
+ # @return [String] lines around the target line within the file
47
+ #
48
+ # @see #lines_around
49
+ def snippet_for(error_line)
50
+ if error_line =~ /(.*):(\d+)/
51
+ file = $1
52
+ line = $2.to_i
53
+ [lines_around(file, line), line]
54
+ else
55
+ ["# Couldn't get snippet for #{error_line}", 1]
56
+ end
57
+ end
58
+
59
+ # @api private
60
+ #
61
+ # Extract lines of code centered around a particular line within a source file.
62
+ #
63
+ # @param [String] file filename
64
+ # @param [Fixnum] line line number
65
+ # @return [String] lines around the target line within the file (2 above and 1 below).
66
+ def lines_around(file, line)
67
+ if File.file?(file)
68
+ lines = File.read(file).split("\n")
69
+ min = [0, line-3].max
70
+ max = [line+1, lines.length-1].min
71
+ selected_lines = []
72
+ selected_lines.join("\n")
73
+ lines[min..max].join("\n")
74
+ else
75
+ "# Couldn't get snippet for #{file}"
76
+ end
77
+ rescue SecurityError
78
+ "# Couldn't get snippet for #{file}"
79
+ end
80
+
81
+ # @api private
82
+ #
83
+ # Adds line numbers to all lines and highlights the line where the failure occurred using html `span` tags.
84
+ #
85
+ # @param [String] highlighted syntax-highlighted snippet surrounding the offending line of code
86
+ # @param [Fixnum] offending_line line where failure occured
87
+ # @return [String] completed snippet
88
+ def post_process(highlighted, offending_line)
89
+ new_lines = []
90
+ highlighted.split("\n").each_with_index do |line, i|
91
+ new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
92
+ new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
93
+ new_lines << new_line
94
+ end
95
+ new_lines.join("\n")
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,54 @@
1
+ # ## Built-in Formatters
2
+ #
3
+ # * progress (default) - prints dots for passing examples, `F` for failures, `*` for pending
4
+ # * documentation - prints the docstrings passed to `describe` and `it` methods (and their aliases)
5
+ # * html
6
+ # * json - useful for archiving data for subsequent analysis
7
+ #
8
+ # The progress formatter is the default, but you can choose any one or more of
9
+ # the other formatters by passing with the `--format` (or `-f` for short)
10
+ # command-line option, e.g.
11
+ #
12
+ # rspec --format documentation
13
+ #
14
+ # You can also send the output of multiple formatters to different streams, e.g.
15
+ #
16
+ # rspec --format documentation --format html --out results.html
17
+ #
18
+ # This example sends the output of the documentation formatter to `$stdout`, and
19
+ # the output of the html formatter to results.html.
20
+ #
21
+ # ## Custom Formatters
22
+ #
23
+ # You can tell RSpec to use a custom formatter by passing its path and name to
24
+ # the `rspec` commmand. For example, if you define MyCustomFormatter in
25
+ # path/to/my_custom_formatter.rb, you would type this command:
26
+ #
27
+ # rspec --require path/to/my_custom_formatter.rb --format MyCustomFormatter
28
+ #
29
+ # The reporter calls every formatter with this protocol:
30
+ #
31
+ # * `start(expected_example_count)`
32
+ # * zero or more of the following
33
+ # * `example_group_started(group)`
34
+ # * `example_started(example)`
35
+ # * `example_passed(example)`
36
+ # * `example_failed(example)`
37
+ # * `example_pending(example)`
38
+ # * `message(string)`
39
+ # * `stop`
40
+ # * `start_dump`
41
+ # * `dump_pending`
42
+ # * `dump_failures`
43
+ # * `dump_summary(duration, example_count, failure_count, pending_count)`
44
+ # * `seed(value)`
45
+ # * `close`
46
+ #
47
+ # You can either implement all of those methods or subclass
48
+ # `RSpec::Core::Formatters::BaseTextFormatter` and override the methods you want
49
+ # to enhance.
50
+ #
51
+ # @see RSpec::Core::Formatters::BaseTextFormatter
52
+ # @see RSpec::Core::Reporter
53
+ module RSpec::Core::Formatters
54
+ end
@@ -0,0 +1,535 @@
1
+ module RSpec
2
+ module Core
3
+ module Hooks
4
+ class Hook
5
+ attr_reader :block, :options
6
+
7
+ def initialize(block, options)
8
+ @block = block
9
+ @options = options
10
+ end
11
+
12
+ def options_apply?(example_or_group)
13
+ example_or_group.all_apply?(options)
14
+ end
15
+ end
16
+
17
+ class BeforeHook < Hook
18
+ def run(example)
19
+ example.instance_exec(example, &block)
20
+ end
21
+
22
+ def display_name
23
+ "before hook"
24
+ end
25
+ end
26
+
27
+ class AfterHook < Hook
28
+ def run(example)
29
+ example.instance_exec_with_rescue("in an after hook", &block)
30
+ end
31
+
32
+ def display_name
33
+ "after hook"
34
+ end
35
+ end
36
+
37
+ class AfterAllHook < Hook
38
+ def run(example)
39
+ example.instance_exec(example, &block)
40
+ rescue Exception => e
41
+ # TODO: come up with a better solution for this.
42
+ RSpec.configuration.reporter.message <<-EOS
43
+
44
+ An error occurred in an after(:all) hook.
45
+ #{e.class}: #{e.message}
46
+ occurred at #{e.backtrace.first}
47
+
48
+ EOS
49
+ end
50
+
51
+ def display_name
52
+ "after(:all) hook"
53
+ end
54
+ end
55
+
56
+ class AroundHook < Hook
57
+ def display_name
58
+ "around hook"
59
+ end
60
+ end
61
+
62
+ class BaseHookCollection
63
+ Array.public_instance_methods(false).each do |name|
64
+ define_method(name) { |*a, &b| hooks.__send__(name, *a, &b) }
65
+ end
66
+
67
+ attr_reader :hooks
68
+ protected :hooks
69
+
70
+ alias append push
71
+ alias prepend unshift
72
+
73
+ def initialize(hooks=[])
74
+ @hooks = hooks
75
+ end
76
+ end
77
+
78
+ class HookCollection < BaseHookCollection
79
+ def for(example_or_group)
80
+ self.class.
81
+ new(hooks.select {|hook| hook.options_apply?(example_or_group)}).
82
+ with(example_or_group)
83
+ end
84
+
85
+ def with(example)
86
+ @example = example
87
+ self
88
+ end
89
+
90
+ def run
91
+ hooks.each {|h| h.run(@example)}
92
+ end
93
+ end
94
+
95
+ class AroundHookCollection < BaseHookCollection
96
+ def for(example, initial_procsy=nil)
97
+ self.class.new(hooks.select {|hook| hook.options_apply?(example)}).
98
+ with(example, initial_procsy)
99
+ end
100
+
101
+ def with(example, initial_procsy)
102
+ @example = example
103
+ @initial_procsy = initial_procsy
104
+ self
105
+ end
106
+
107
+ def run
108
+ hooks.reduce(@initial_procsy) do |procsy, around_hook|
109
+ procsy.wrap do
110
+ @example.instance_exec(procsy, &around_hook.block)
111
+ end
112
+ end.call
113
+ end
114
+ end
115
+
116
+ class GroupHookCollection < BaseHookCollection
117
+ def for(group)
118
+ @group = group
119
+ self
120
+ end
121
+
122
+ def run
123
+ hooks.shift.run(@group) until hooks.empty?
124
+ end
125
+ end
126
+
127
+ class HookCollections
128
+ def initialize(data)
129
+ @data = data
130
+ end
131
+
132
+ def [](key)
133
+ @data[key]
134
+ end
135
+
136
+ def register_globals(host, globals)
137
+ process(host, globals, :before, :each)
138
+ process(host, globals, :after, :each)
139
+ process(host, globals, :around, :each)
140
+
141
+ process(host, globals, :before, :all)
142
+ process(host, globals, :after, :all)
143
+ end
144
+
145
+ private
146
+
147
+ def process(host, globals, position, scope)
148
+ globals[position][scope].each do |hook|
149
+ next unless scope == :each || hook.options_apply?(host)
150
+ next if host.parent_groups.any? {|a| a.hooks[position][scope].include?(hook)}
151
+ self[position][scope] << hook
152
+ end
153
+ end
154
+ end
155
+
156
+ # @private
157
+ def hooks
158
+ @hooks ||= HookCollections.new(
159
+ :around => { :each => AroundHookCollection.new },
160
+ :before => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new },
161
+ :after => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new }
162
+ )
163
+ end
164
+
165
+ # @api public
166
+ # @overload before(&block)
167
+ # @overload before(scope, &block)
168
+ # @overload before(scope, conditions, &block)
169
+ # @overload before(conditions, &block)
170
+ #
171
+ # @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
172
+ # @param [Hash] conditions
173
+ # constrains this hook to examples matching these conditions e.g.
174
+ # `before(:each, :ui => true) { ... }` will only run with examples or
175
+ # groups declared with `:ui => true`.
176
+ #
177
+ # @see #after
178
+ # @see #around
179
+ # @see ExampleGroup
180
+ # @see SharedContext
181
+ # @see SharedExampleGroup
182
+ # @see Configuration
183
+ #
184
+ # Declare a block of code to be run before each example (using `:each`)
185
+ # or once before any example (using `:all`). These are usually declared
186
+ # directly in the {ExampleGroup} to which they apply, but they can also
187
+ # be shared across multiple groups.
188
+ #
189
+ # You can also use `before(:suite)` to run a block of code before any
190
+ # example groups are run. This should be declared in {RSpec.configure}
191
+ #
192
+ # Instance variables declared in `before(:each)` or `before(:all)` are
193
+ # accessible within each example.
194
+ #
195
+ # ### Order
196
+ #
197
+ # `before` hooks are stored in three scopes, which are run in order:
198
+ # `:suite`, `:all`, and `:each`. They can also be declared in several
199
+ # different places: `RSpec.configure`, a parent group, the current group.
200
+ # They are run in the following order:
201
+ #
202
+ # before(:suite) # declared in RSpec.configure
203
+ # before(:all) # declared in RSpec.configure
204
+ # before(:all) # declared in a parent group
205
+ # before(:all) # declared in the current group
206
+ # before(:each) # declared in RSpec.configure
207
+ # before(:each) # declared in a parent group
208
+ # before(:each) # declared in the current group
209
+ #
210
+ # If more than one `before` is declared within any one scope, they are run
211
+ # in the order in which they are declared.
212
+ #
213
+ # ### Conditions
214
+ #
215
+ # When you add a conditions hash to `before(:each)` or `before(:all)`,
216
+ # RSpec will only apply that hook to groups or examples that match the
217
+ # conditions. e.g.
218
+ #
219
+ # RSpec.configure do |config|
220
+ # config.before(:each, :authorized => true) do
221
+ # log_in_as :authorized_user
222
+ # end
223
+ # end
224
+ #
225
+ # describe Something, :authorized => true do
226
+ # # the before hook will run in before each example in this group
227
+ # end
228
+ #
229
+ # describe SomethingElse do
230
+ # it "does something", :authorized => true do
231
+ # # the before hook will run before this example
232
+ # end
233
+ #
234
+ # it "does something else" do
235
+ # # the hook will not run before this example
236
+ # end
237
+ # end
238
+ #
239
+ # ### Warning: `before(:suite, :with => :conditions)`
240
+ #
241
+ # The conditions hash is used to match against specific examples. Since
242
+ # `before(:suite)` is not run in relation to any specific example or
243
+ # group, conditions passed along with `:suite` are effectively ignored.
244
+ #
245
+ # ### Exceptions
246
+ #
247
+ # When an exception is raised in a `before` block, RSpec skips any
248
+ # subsequent `before` blocks and the example, but runs all of the
249
+ # `after(:each)` and `after(:all)` hooks.
250
+ #
251
+ # ### Warning: implicit before blocks
252
+ #
253
+ # `before` hooks can also be declared in shared contexts which get
254
+ # included implicitly either by you or by extension libraries. Since
255
+ # RSpec runs these in the order in which they are declared within each
256
+ # scope, load order matters, and can lead to confusing results when one
257
+ # before block depends on state that is prepared in another before block
258
+ # that gets run later.
259
+ #
260
+ # ### Warning: `before(:all)`
261
+ #
262
+ # It is very tempting to use `before(:all)` to speed things up, but we
263
+ # recommend that you avoid this as there are a number of gotchas, as well
264
+ # as things that simply don't work.
265
+ #
266
+ # #### context
267
+ #
268
+ # `before(:all)` is run in an example that is generated to provide group
269
+ # context for the block.
270
+ #
271
+ # #### instance variables
272
+ #
273
+ # Instance variables declared in `before(:all)` are shared across all the
274
+ # examples in the group. This means that each example can change the
275
+ # state of a shared object, resulting in an ordering dependency that can
276
+ # make it difficult to reason about failures.
277
+ #
278
+ # #### unsupported rspec constructs
279
+ #
280
+ # RSpec has several constructs that reset state between each example
281
+ # automatically. These are not intended for use from within `before(:all)`:
282
+ #
283
+ # * `let` declarations
284
+ # * `subject` declarations
285
+ # * Any mocking, stubbing or test double declaration
286
+ #
287
+ # ### other frameworks
288
+ #
289
+ # Mock object frameworks and database transaction managers (like
290
+ # ActiveRecord) are typically designed around the idea of setting up
291
+ # before an example, running that one example, and then tearing down.
292
+ # This means that mocks and stubs can (sometimes) be declared in
293
+ # `before(:all)`, but get torn down before the first real example is ever
294
+ # run.
295
+ #
296
+ # You _can_ create database-backed model objects in a `before(:all)` in
297
+ # rspec-rails, but it will not be wrapped in a transaction for you, so
298
+ # you are on your own to clean up in an `after(:all)` block.
299
+ #
300
+ # @example before(:each) declared in an {ExampleGroup}
301
+ #
302
+ # describe Thing do
303
+ # before(:each) do
304
+ # @thing = Thing.new
305
+ # end
306
+ #
307
+ # it "does something" do
308
+ # # here you can access @thing
309
+ # end
310
+ # end
311
+ #
312
+ # @example before(:all) declared in an {ExampleGroup}
313
+ #
314
+ # describe Parser do
315
+ # before(:all) do
316
+ # File.open(file_to_parse, 'w') do |f|
317
+ # f.write <<-CONTENT
318
+ # stuff in the file
319
+ # CONTENT
320
+ # end
321
+ # end
322
+ #
323
+ # it "parses the file" do
324
+ # Parser.parse(file_to_parse)
325
+ # end
326
+ #
327
+ # after(:all) do
328
+ # File.delete(file_to_parse)
329
+ # end
330
+ # end
331
+ def before(*args, &block)
332
+ register_hook :append, :before, *args, &block
333
+ end
334
+
335
+ alias_method :append_before, :before
336
+
337
+ # Adds `block` to the front of the list of `before` blocks in the same
338
+ # scope (`:each`, `:all`, or `:suite`).
339
+ #
340
+ # See #before for scoping semantics.
341
+ def prepend_before(*args, &block)
342
+ register_hook :prepend, :before, *args, &block
343
+ end
344
+
345
+ # @api public
346
+ # @overload after(&block)
347
+ # @overload after(scope, &block)
348
+ # @overload after(scope, conditions, &block)
349
+ # @overload after(conditions, &block)
350
+ #
351
+ # @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
352
+ # @param [Hash] conditions
353
+ # constrains this hook to examples matching these conditions e.g.
354
+ # `after(:each, :ui => true) { ... }` will only run with examples or
355
+ # groups declared with `:ui => true`.
356
+ #
357
+ # @see #before
358
+ # @see #around
359
+ # @see ExampleGroup
360
+ # @see SharedContext
361
+ # @see SharedExampleGroup
362
+ # @see Configuration
363
+ #
364
+ # Declare a block of code to be run after each example (using `:each`) or
365
+ # once after all examples (using `:all`). See
366
+ # [#before](Hooks#before-instance_method) for more information about
367
+ # ordering.
368
+ #
369
+ # ### Exceptions
370
+ #
371
+ # `after` hooks are guaranteed to run even when there are exceptions in
372
+ # `before` hooks or examples. When an exception is raised in an after
373
+ # block, the exception is captured for later reporting, and subsequent
374
+ # `after` blocks are run.
375
+ #
376
+ # ### Order
377
+ #
378
+ # `after` hooks are stored in three scopes, which are run in order:
379
+ # `:each`, `:all`, and `:suite`. They can also be declared in several
380
+ # different places: `RSpec.configure`, a parent group, the current group.
381
+ # They are run in the following order:
382
+ #
383
+ # after(:each) # declared in the current group
384
+ # after(:each) # declared in a parent group
385
+ # after(:each) # declared in RSpec.configure
386
+ # after(:all) # declared in the current group
387
+ # after(:all) # declared in a parent group
388
+ # after(:all) # declared in RSpec.configure
389
+ #
390
+ # This is the reverse of the order in which `before` hooks are run.
391
+ # Similarly, if more than one `after` is declared within any one scope,
392
+ # they are run in reverse order of that in which they are declared.
393
+ def after(*args, &block)
394
+ register_hook :prepend, :after, *args, &block
395
+ end
396
+
397
+ alias_method :prepend_after, :after
398
+
399
+ # Adds `block` to the back of the list of `after` blocks in the same
400
+ # scope (`:each`, `:all`, or `:suite`).
401
+ #
402
+ # See #after for scoping semantics.
403
+ def append_after(*args, &block)
404
+ register_hook :append, :after, *args, &block
405
+ end
406
+
407
+ # @api public
408
+ # @overload around(&block)
409
+ # @overload around(scope, &block)
410
+ # @overload around(scope, conditions, &block)
411
+ # @overload around(conditions, &block)
412
+ #
413
+ # @param [Symbol] scope `:each` (defaults to `:each`)
414
+ # present for syntax parity with `before` and `after`, but `:each` is
415
+ # the only supported value.
416
+ #
417
+ # @param [Hash] conditions
418
+ # constrains this hook to examples matching these conditions e.g.
419
+ # `around(:each, :ui => true) { ... }` will only run with examples or
420
+ # groups declared with `:ui => true`.
421
+ #
422
+ # @yield [Example] the example to run
423
+ #
424
+ # @note the syntax of `around` is similar to that of `before` and `after`
425
+ # but the semantics are quite different. `before` and `after` hooks are
426
+ # run in the context of of the examples with which they are associated,
427
+ # whereas `around` hooks are actually responsible for running the
428
+ # examples. Consequently, `around` hooks do not have direct access to
429
+ # resources that are made available within the examples and their
430
+ # associated `before` and `after` hooks.
431
+ #
432
+ # @note `:each` is the only supported scope.
433
+ #
434
+ # Declare a block of code, parts of which will be run before and parts
435
+ # after the example. It is your responsibility to run the example:
436
+ #
437
+ # around(:each) do |ex|
438
+ # # do some stuff before
439
+ # ex.run
440
+ # # do some stuff after
441
+ # end
442
+ #
443
+ # The yielded example aliases `run` with `call`, which lets you treat it
444
+ # like a `Proc`. This is especially handy when working with libaries
445
+ # that manage their own setup and teardown using a block or proc syntax,
446
+ # e.g.
447
+ #
448
+ # around(:each) {|ex| Database.transaction(&ex)}
449
+ # around(:each) {|ex| FakeFS(&ex)}
450
+ #
451
+ def around(*args, &block)
452
+ register_hook :prepend, :around, *args, &block
453
+ end
454
+
455
+ # @private
456
+ #
457
+ # Runs all of the blocks stored with the hook in the context of the
458
+ # example. If no example is provided, just calls the hook directly.
459
+ def run_hook(hook, scope, example_or_group=ExampleGroup.new, initial_procsy=nil)
460
+ return if RSpec.configuration.dry_run?
461
+
462
+ find_hook(hook, scope, example_or_group, initial_procsy).run
463
+ end
464
+
465
+ # @private
466
+ def around_each_hooks_for(example, initial_procsy=nil)
467
+ AroundHookCollection.new(FlatMap.flat_map(parent_groups) {|a| a.hooks[:around][:each]}).for(example, initial_procsy)
468
+ end
469
+
470
+ private
471
+
472
+ SCOPES = [:each, :all, :suite]
473
+
474
+ HOOK_TYPES = {
475
+ :before => Hash.new { BeforeHook },
476
+ :after => Hash.new { AfterHook },
477
+ :around => Hash.new { AroundHook }
478
+ }
479
+
480
+ HOOK_TYPES[:after][:all] = AfterAllHook
481
+
482
+ def before_all_hooks_for(group)
483
+ GroupHookCollection.new(hooks[:before][:all]).for(group)
484
+ end
485
+
486
+ def after_all_hooks_for(group)
487
+ GroupHookCollection.new(hooks[:after][:all]).for(group)
488
+ end
489
+
490
+ def before_each_hooks_for(example)
491
+ HookCollection.new(FlatMap.flat_map(parent_groups.reverse) {|a| a.hooks[:before][:each]}).for(example)
492
+ end
493
+
494
+ def after_each_hooks_for(example)
495
+ HookCollection.new(FlatMap.flat_map(parent_groups) {|a| a.hooks[:after][:each]}).for(example)
496
+ end
497
+
498
+ def register_hook prepend_or_append, hook, *args, &block
499
+ scope, options = scope_and_options_from(*args)
500
+ hooks[hook][scope].send(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
501
+ end
502
+
503
+ def find_hook(hook, scope, example_or_group, initial_procsy)
504
+ case [hook, scope]
505
+ when [:before, :all]
506
+ before_all_hooks_for(example_or_group)
507
+ when [:after, :all]
508
+ after_all_hooks_for(example_or_group)
509
+ when [:around, :each]
510
+ around_each_hooks_for(example_or_group, initial_procsy)
511
+ when [:before, :each]
512
+ before_each_hooks_for(example_or_group)
513
+ when [:after, :each]
514
+ after_each_hooks_for(example_or_group)
515
+ when [:before, :suite], [:after, :suite]
516
+ hooks[hook][:suite].with(example_or_group)
517
+ end
518
+ end
519
+
520
+ def scope_and_options_from(*args)
521
+ return extract_scope_from(args), Metadata.build_hash_from(args)
522
+ end
523
+
524
+ def extract_scope_from(args)
525
+ if SCOPES.include?(args.first)
526
+ args.shift
527
+ elsif args.any? { |a| a.is_a?(Symbol) }
528
+ raise ArgumentError.new("You must explicitly give a scope (:each, :all, or :suite) when using symbols as metadata for a hook.")
529
+ else
530
+ :each
531
+ end
532
+ end
533
+ end
534
+ end
535
+ end