opal-rspec-cj 0.4.4

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.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.gitmodules +15 -0
  4. data/.travis.yml +13 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +25 -0
  7. data/Gemfile +8 -0
  8. data/README.md +147 -0
  9. data/Rakefile +26 -0
  10. data/config.ru +10 -0
  11. data/example/Gemfile +4 -0
  12. data/example/README.md +13 -0
  13. data/example/Rakefile +8 -0
  14. data/example/opal/user.rb +11 -0
  15. data/example/spec/user_spec.rb +15 -0
  16. data/lib/opal-rspec.rb +2 -0
  17. data/lib/opal/rspec.rb +20 -0
  18. data/lib/opal/rspec/rake_task.rb +63 -0
  19. data/lib/opal/rspec/version.rb +5 -0
  20. data/opal-rspec.gemspec +21 -0
  21. data/opal/opal-rspec.rb +1 -0
  22. data/opal/opal/rspec.rb +25 -0
  23. data/opal/opal/rspec/async.rb +289 -0
  24. data/opal/opal/rspec/browser_formatter.rb +188 -0
  25. data/opal/opal/rspec/fixes.rb +116 -0
  26. data/opal/opal/rspec/requires.rb +45 -0
  27. data/opal/opal/rspec/runner.rb +69 -0
  28. data/opal/opal/rspec/sprockets_runner.rb.erb +11 -0
  29. data/opal/opal/rspec/text_formatter.rb +74 -0
  30. data/spec/async_spec.rb +38 -0
  31. data/spec/example_spec.rb +163 -0
  32. data/spec/matchers_spec.rb +201 -0
  33. data/spec/mock_spec.rb +63 -0
  34. data/spec/named_subject_spec.rb +11 -0
  35. data/spec/should_syntax_spec.rb +17 -0
  36. data/vendor/spec_runner.js +50 -0
  37. data/vendor_lib/rspec-expectations.rb +1 -0
  38. data/vendor_lib/rspec.rb +3 -0
  39. data/vendor_lib/rspec/autorun.rb +2 -0
  40. data/vendor_lib/rspec/core.rb +203 -0
  41. data/vendor_lib/rspec/core/backport_random.rb +302 -0
  42. data/vendor_lib/rspec/core/backtrace_formatter.rb +65 -0
  43. data/vendor_lib/rspec/core/command_line.rb +36 -0
  44. data/vendor_lib/rspec/core/configuration.rb +1129 -0
  45. data/vendor_lib/rspec/core/configuration_options.rb +143 -0
  46. data/vendor_lib/rspec/core/drb_command_line.rb +26 -0
  47. data/vendor_lib/rspec/core/drb_options.rb +87 -0
  48. data/vendor_lib/rspec/core/dsl.rb +26 -0
  49. data/vendor_lib/rspec/core/example.rb +312 -0
  50. data/vendor_lib/rspec/core/example_group.rb +540 -0
  51. data/vendor_lib/rspec/core/filter_manager.rb +224 -0
  52. data/vendor_lib/rspec/core/flat_map.rb +17 -0
  53. data/vendor_lib/rspec/core/formatters.rb +54 -0
  54. data/vendor_lib/rspec/core/formatters/base_formatter.rb +291 -0
  55. data/vendor_lib/rspec/core/formatters/base_text_formatter.rb +307 -0
  56. data/vendor_lib/rspec/core/formatters/deprecation_formatter.rb +193 -0
  57. data/vendor_lib/rspec/core/formatters/documentation_formatter.rb +67 -0
  58. data/vendor_lib/rspec/core/formatters/helpers.rb +82 -0
  59. data/vendor_lib/rspec/core/formatters/html_formatter.rb +155 -0
  60. data/vendor_lib/rspec/core/formatters/html_printer.rb +408 -0
  61. data/vendor_lib/rspec/core/formatters/json_formatter.rb +99 -0
  62. data/vendor_lib/rspec/core/formatters/progress_formatter.rb +32 -0
  63. data/vendor_lib/rspec/core/formatters/snippet_extractor.rb +101 -0
  64. data/vendor_lib/rspec/core/hooks.rb +535 -0
  65. data/vendor_lib/rspec/core/memoized_helpers.rb +431 -0
  66. data/vendor_lib/rspec/core/metadata.rb +313 -0
  67. data/vendor_lib/rspec/core/mocking/with_absolutely_nothing.rb +11 -0
  68. data/vendor_lib/rspec/core/mocking/with_flexmock.rb +27 -0
  69. data/vendor_lib/rspec/core/mocking/with_mocha.rb +52 -0
  70. data/vendor_lib/rspec/core/mocking/with_rr.rb +27 -0
  71. data/vendor_lib/rspec/core/mocking/with_rspec.rb +27 -0
  72. data/vendor_lib/rspec/core/option_parser.rb +234 -0
  73. data/vendor_lib/rspec/core/ordering.rb +154 -0
  74. data/vendor_lib/rspec/core/pending.rb +110 -0
  75. data/vendor_lib/rspec/core/project_initializer.rb +88 -0
  76. data/vendor_lib/rspec/core/rake_task.rb +128 -0
  77. data/vendor_lib/rspec/core/reporter.rb +132 -0
  78. data/vendor_lib/rspec/core/ruby_project.rb +44 -0
  79. data/vendor_lib/rspec/core/runner.rb +97 -0
  80. data/vendor_lib/rspec/core/shared_context.rb +53 -0
  81. data/vendor_lib/rspec/core/shared_example_group.rb +146 -0
  82. data/vendor_lib/rspec/core/shared_example_group/collection.rb +27 -0
  83. data/vendor_lib/rspec/core/version.rb +7 -0
  84. data/vendor_lib/rspec/core/warnings.rb +22 -0
  85. data/vendor_lib/rspec/core/world.rb +131 -0
  86. data/vendor_lib/rspec/expectations.rb +75 -0
  87. data/vendor_lib/rspec/expectations/differ.rb +154 -0
  88. data/vendor_lib/rspec/expectations/errors.rb +9 -0
  89. data/vendor_lib/rspec/expectations/expectation_target.rb +87 -0
  90. data/vendor_lib/rspec/expectations/extensions.rb +1 -0
  91. data/vendor_lib/rspec/expectations/extensions/object.rb +29 -0
  92. data/vendor_lib/rspec/expectations/fail_with.rb +79 -0
  93. data/vendor_lib/rspec/expectations/handler.rb +68 -0
  94. data/vendor_lib/rspec/expectations/syntax.rb +182 -0
  95. data/vendor_lib/rspec/expectations/version.rb +8 -0
  96. data/vendor_lib/rspec/matchers.rb +633 -0
  97. data/vendor_lib/rspec/matchers/built_in.rb +39 -0
  98. data/vendor_lib/rspec/matchers/built_in/base_matcher.rb +68 -0
  99. data/vendor_lib/rspec/matchers/built_in/be.rb +213 -0
  100. data/vendor_lib/rspec/matchers/built_in/be_instance_of.rb +15 -0
  101. data/vendor_lib/rspec/matchers/built_in/be_kind_of.rb +11 -0
  102. data/vendor_lib/rspec/matchers/built_in/be_within.rb +55 -0
  103. data/vendor_lib/rspec/matchers/built_in/change.rb +141 -0
  104. data/vendor_lib/rspec/matchers/built_in/cover.rb +21 -0
  105. data/vendor_lib/rspec/matchers/built_in/eq.rb +22 -0
  106. data/vendor_lib/rspec/matchers/built_in/eql.rb +23 -0
  107. data/vendor_lib/rspec/matchers/built_in/equal.rb +48 -0
  108. data/vendor_lib/rspec/matchers/built_in/exist.rb +26 -0
  109. data/vendor_lib/rspec/matchers/built_in/has.rb +48 -0
  110. data/vendor_lib/rspec/matchers/built_in/include.rb +61 -0
  111. data/vendor_lib/rspec/matchers/built_in/match.rb +17 -0
  112. data/vendor_lib/rspec/matchers/built_in/match_array.rb +51 -0
  113. data/vendor_lib/rspec/matchers/built_in/raise_error.rb +154 -0
  114. data/vendor_lib/rspec/matchers/built_in/respond_to.rb +74 -0
  115. data/vendor_lib/rspec/matchers/built_in/satisfy.rb +30 -0
  116. data/vendor_lib/rspec/matchers/built_in/start_and_end_with.rb +48 -0
  117. data/vendor_lib/rspec/matchers/built_in/throw_symbol.rb +94 -0
  118. data/vendor_lib/rspec/matchers/built_in/yield.rb +297 -0
  119. data/vendor_lib/rspec/matchers/compatibility.rb +14 -0
  120. data/vendor_lib/rspec/matchers/configuration.rb +113 -0
  121. data/vendor_lib/rspec/matchers/dsl.rb +23 -0
  122. data/vendor_lib/rspec/matchers/generated_descriptions.rb +35 -0
  123. data/vendor_lib/rspec/matchers/matcher.rb +301 -0
  124. data/vendor_lib/rspec/matchers/method_missing.rb +12 -0
  125. data/vendor_lib/rspec/matchers/operator_matcher.rb +99 -0
  126. data/vendor_lib/rspec/matchers/pretty.rb +70 -0
  127. data/vendor_lib/rspec/matchers/test_unit_integration.rb +11 -0
  128. data/vendor_lib/rspec/mocks.rb +100 -0
  129. data/vendor_lib/rspec/mocks/any_instance/chain.rb +92 -0
  130. data/vendor_lib/rspec/mocks/any_instance/expectation_chain.rb +47 -0
  131. data/vendor_lib/rspec/mocks/any_instance/message_chains.rb +75 -0
  132. data/vendor_lib/rspec/mocks/any_instance/recorder.rb +200 -0
  133. data/vendor_lib/rspec/mocks/any_instance/stub_chain.rb +45 -0
  134. data/vendor_lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  135. data/vendor_lib/rspec/mocks/argument_list_matcher.rb +104 -0
  136. data/vendor_lib/rspec/mocks/argument_matchers.rb +264 -0
  137. data/vendor_lib/rspec/mocks/arity_calculator.rb +66 -0
  138. data/vendor_lib/rspec/mocks/configuration.rb +111 -0
  139. data/vendor_lib/rspec/mocks/error_generator.rb +203 -0
  140. data/vendor_lib/rspec/mocks/errors.rb +12 -0
  141. data/vendor_lib/rspec/mocks/example_methods.rb +201 -0
  142. data/vendor_lib/rspec/mocks/extensions/marshal.rb +17 -0
  143. data/vendor_lib/rspec/mocks/framework.rb +36 -0
  144. data/vendor_lib/rspec/mocks/instance_method_stasher.rb +112 -0
  145. data/vendor_lib/rspec/mocks/matchers/have_received.rb +99 -0
  146. data/vendor_lib/rspec/mocks/matchers/receive.rb +112 -0
  147. data/vendor_lib/rspec/mocks/matchers/receive_messages.rb +72 -0
  148. data/vendor_lib/rspec/mocks/message_expectation.rb +643 -0
  149. data/vendor_lib/rspec/mocks/method_double.rb +209 -0
  150. data/vendor_lib/rspec/mocks/method_reference.rb +95 -0
  151. data/vendor_lib/rspec/mocks/mock.rb +7 -0
  152. data/vendor_lib/rspec/mocks/mutate_const.rb +406 -0
  153. data/vendor_lib/rspec/mocks/object_reference.rb +90 -0
  154. data/vendor_lib/rspec/mocks/order_group.rb +82 -0
  155. data/vendor_lib/rspec/mocks/proxy.rb +269 -0
  156. data/vendor_lib/rspec/mocks/proxy_for_nil.rb +37 -0
  157. data/vendor_lib/rspec/mocks/space.rb +95 -0
  158. data/vendor_lib/rspec/mocks/standalone.rb +3 -0
  159. data/vendor_lib/rspec/mocks/stub_chain.rb +51 -0
  160. data/vendor_lib/rspec/mocks/syntax.rb +374 -0
  161. data/vendor_lib/rspec/mocks/targets.rb +90 -0
  162. data/vendor_lib/rspec/mocks/test_double.rb +109 -0
  163. data/vendor_lib/rspec/mocks/verifying_double.rb +77 -0
  164. data/vendor_lib/rspec/mocks/verifying_message_expecation.rb +60 -0
  165. data/vendor_lib/rspec/mocks/verifying_proxy.rb +151 -0
  166. data/vendor_lib/rspec/mocks/version.rb +7 -0
  167. data/vendor_lib/rspec/support.rb +6 -0
  168. data/vendor_lib/rspec/support/caller_filter.rb +56 -0
  169. data/vendor_lib/rspec/support/spec.rb +14 -0
  170. data/vendor_lib/rspec/support/spec/deprecation_helpers.rb +29 -0
  171. data/vendor_lib/rspec/support/spec/in_sub_process.rb +40 -0
  172. data/vendor_lib/rspec/support/spec/stderr_splitter.rb +50 -0
  173. data/vendor_lib/rspec/support/version.rb +7 -0
  174. data/vendor_lib/rspec/support/warnings.rb +41 -0
  175. data/vendor_lib/rspec/version.rb +5 -0
  176. metadata +268 -0
@@ -0,0 +1,99 @@
1
+ require 'rspec/core/formatters/base_formatter'
2
+ require 'json'
3
+
4
+ module RSpec
5
+ module Core
6
+ module Formatters
7
+
8
+ class JsonFormatter < BaseFormatter
9
+
10
+ attr_reader :output_hash
11
+
12
+ def initialize(output)
13
+ super
14
+ @output_hash = {}
15
+ end
16
+
17
+ def message(message)
18
+ (@output_hash[:messages] ||= []) << message
19
+ end
20
+
21
+ def dump_summary(duration, example_count, failure_count, pending_count)
22
+ super(duration, example_count, failure_count, pending_count)
23
+ @output_hash[:summary] = {
24
+ :duration => duration,
25
+ :example_count => example_count,
26
+ :failure_count => failure_count,
27
+ :pending_count => pending_count
28
+ }
29
+ @output_hash[:summary_line] = summary_line(example_count, failure_count, pending_count)
30
+
31
+ dump_profile unless mute_profile_output?(failure_count)
32
+ end
33
+
34
+ def summary_line(example_count, failure_count, pending_count)
35
+ summary = pluralize(example_count, "example")
36
+ summary << ", " << pluralize(failure_count, "failure")
37
+ summary << ", #{pending_count} pending" if pending_count > 0
38
+ summary
39
+ end
40
+
41
+ def stop
42
+ super
43
+ @output_hash[:examples] = examples.map do |example|
44
+ format_example(example).tap do |hash|
45
+ if e=example.exception
46
+ hash[:exception] = {
47
+ :class => e.class.name,
48
+ :message => e.message,
49
+ :backtrace => e.backtrace,
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def close
57
+ output.write @output_hash.to_json
58
+ output.close if IO === output && output != $stdout
59
+ end
60
+
61
+ def dump_profile
62
+ @output_hash[:profile] = {}
63
+ dump_profile_slowest_examples
64
+ dump_profile_slowest_example_groups
65
+ end
66
+
67
+ def dump_profile_slowest_examples
68
+ @output_hash[:profile] = {}
69
+ sorted_examples = slowest_examples
70
+ @output_hash[:profile][:examples] = sorted_examples[:examples].map do |example|
71
+ format_example(example).tap do |hash|
72
+ hash[:run_time] = example.execution_result[:run_time]
73
+ end
74
+ end
75
+ @output_hash[:profile][:slowest] = sorted_examples[:slows]
76
+ @output_hash[:profile][:total] = sorted_examples[:total]
77
+ end
78
+
79
+ def dump_profile_slowest_example_groups
80
+ @output_hash[:profile] ||= {}
81
+ @output_hash[:profile][:groups] = slowest_groups.map do |loc, hash|
82
+ hash.update(:location => loc)
83
+ end
84
+ end
85
+
86
+ private
87
+ def format_example(example)
88
+ {
89
+ :description => example.description,
90
+ :full_description => example.full_description,
91
+ :status => example.execution_result[:status],
92
+ :file_path => example.metadata[:file_path],
93
+ :line_number => example.metadata[:line_number]
94
+ }
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,32 @@
1
+ require 'rspec/core/formatters/base_text_formatter'
2
+ module RSpec
3
+ module Core
4
+ module Formatters
5
+
6
+ class ProgressFormatter < BaseTextFormatter
7
+
8
+ def example_passed(example)
9
+ super(example)
10
+ output.print success_color('.')
11
+ end
12
+
13
+ def example_pending(example)
14
+ super(example)
15
+ output.print pending_color('*')
16
+ end
17
+
18
+ def example_failed(example)
19
+ super(example)
20
+ output.print failure_color('F')
21
+ end
22
+
23
+ def start_dump
24
+ super()
25
+ output.puts
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -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,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