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 @@
1
+ require 'opal/rspec'
@@ -0,0 +1,25 @@
1
+ class MiniTest
2
+ class Unit; end
3
+ end
4
+
5
+ Test = MiniTest
6
+
7
+ require 'opal/rspec/requires'
8
+ require 'opal/rspec/fixes'
9
+ require 'opal/rspec/text_formatter'
10
+ require 'opal/rspec/browser_formatter'
11
+ require 'opal/rspec/runner'
12
+ require 'opal/rspec/async'
13
+
14
+ RSpec.configure do |config|
15
+ # For now, always use our custom formatter for results
16
+ config.formatter = Opal::RSpec::Runner.default_formatter
17
+
18
+ # Async helpers for specs
19
+ config.include Opal::RSpec::AsyncHelpers
20
+
21
+ # Always support expect() and .should syntax (we should not do this really..)
22
+ config.expect_with :rspec do |c|
23
+ c.syntax = [:should, :expect]
24
+ end
25
+ end
@@ -0,0 +1,289 @@
1
+ module Opal
2
+ module RSpec
3
+ # {AsyncHelpers} is automatically included in all example groups to add
4
+ # support for running specs async. Usually, rspec runners expect all
5
+ # examples to run synchronously, but this is not ideal in the case for
6
+ # Opal where a lot of underlying libraries expect the ability to run code
7
+ # in an asynchronous manner.
8
+ #
9
+ # This module defines an {AsyncHelpers::ClassMethods.async} method which
10
+ # can be used instead of `it` inside an example group, which marks the
11
+ # example as being async. This makes the runner wait for the example to
12
+ # complete.
13
+ #
14
+ # describe "Some examples" do
15
+ # it "normal example" do
16
+ # # normal test code
17
+ # end
18
+ #
19
+ # async "async example" do
20
+ # # this will wait until completion before moving on
21
+ # end
22
+ # end
23
+ #
24
+ # Marking an example as being async is only half the task. Examples will
25
+ # also have an instance {AsyncHelpers#async} method defined which is then
26
+ # used to complete the example. Any code run inside this block will run
27
+ # inside the context of the example.
28
+ #
29
+ # describe "HTTP requests" do
30
+ # async "might take a while" do
31
+ # HTTP.get("/url/to/get") do |res|
32
+ # async { expect(res).to be_ok }
33
+ # end
34
+ # end
35
+ # end
36
+ #
37
+ # As soon as `async` is run inside the block, the example completes. This
38
+ # means that only 1 `async` call is allowed. However, you can use `async`
39
+ # multiple times aslong as it is only called once:
40
+ #
41
+ # describe "HTTP requests" do
42
+ # async "should work" do
43
+ # HTTP.get("/users/1").then |res|
44
+ # async { expect(res).to be_ok }
45
+ # end.fail do
46
+ # async { raise "this should not be called" }
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # Here, a promise will either be accepted or rejected, so an `async` block
52
+ # can be used in each case as only 1 will be called.
53
+ #
54
+ # Another helper, {AsyncHelpers#delay} can also be used to run a block of
55
+ # code after a given time in seconds. This is useful to wait for animations
56
+ # or time restricted operations to occur.
57
+ module AsyncHelpers
58
+ module ClassMethods
59
+ # Define an async example method. This should be used instead of `it`
60
+ # to inform the spec runner that the example will need to wait for an
61
+ # {AsyncHelpers#async} method to complete the test. Any additional
62
+ # configuration options can be passed to this call, and they just get
63
+ # delegated to the underlying `#it` call.
64
+ #
65
+ # @example
66
+ # describe "Some tests" do
67
+ # async "should be async" do
68
+ # # ... async code
69
+ # end
70
+ #
71
+ # it "should work with normal tests" do
72
+ # expect(1).to eq(1)
73
+ # end
74
+ # end
75
+ #
76
+ # @param desc [String] description
77
+ def async(desc, *args, &block)
78
+ options = ::RSpec::Core::Metadata.build_hash_from(args)
79
+ Opal::RSpec::AsyncExample.register(self, desc, options, block)
80
+ end
81
+ end
82
+
83
+ def self.included(base)
84
+ base.extend ClassMethods
85
+ end
86
+
87
+ # Must be used with {ClassMethods#async} to finish the async action. If
88
+ # this is not called inside the body then the spec runner will time out
89
+ # or the error might give a false positive as it is not caught inside
90
+ # the current example.
91
+ #
92
+ # @example Complete expectation after HTTP request
93
+ # describe "HTTP calls" do
94
+ # async "complete eventually" do
95
+ # HTTP.get("/some_url") do |response|
96
+ # async { expect(response).to be_ok }
97
+ # end
98
+ # end
99
+ # end
100
+ #
101
+ def async(&block)
102
+ @example.continue_async(block)
103
+ end
104
+
105
+ # Runs the given block after a given duration. You are still required to
106
+ # use a {#async} block inside the delayed callback. This helper can be
107
+ # used to simulate IO delays, or just to wait for animations/other
108
+ # behaviour to finish.
109
+ #
110
+ # The `duaration` should be given in seconds, i.e. `1` means 1 second, or
111
+ # 0.3 means 300ms. The given block is just run after the time delay.
112
+ #
113
+ # @example
114
+ # describe "Some interaction" do
115
+ # async "takes a while to complete" do
116
+ # task = start_long_task!
117
+ #
118
+ # delay(1) do
119
+ # async { expect(task).to be_completed }
120
+ # end
121
+ # end
122
+ # end
123
+ #
124
+ # @param duration [Integer, Float] time in seconds to wait
125
+ def delay(duration, &block)
126
+ `setTimeout(block, duration * 1000)`
127
+ self
128
+ end
129
+
130
+ # Use {#async} instead.
131
+ #
132
+ # @deprecated
133
+ def run_async(&block)
134
+ async(&block)
135
+ end
136
+
137
+ # Use {#delay} instead.
138
+ #
139
+ # @deprecated
140
+ def set_timeout(*args, &block)
141
+ delay(*args, &block)
142
+ end
143
+ end
144
+
145
+ # Runs all async examples from {AsyncExample.examples}.
146
+ class AsyncRunner
147
+ def initialize(runner, reporter, finish_block)
148
+ @runner = runner
149
+ @reporter = reporter
150
+ @finish_block = finish_block
151
+ end
152
+
153
+ def run
154
+ @examples = AsyncExample.examples.clone
155
+ run_next_example
156
+ end
157
+
158
+ def run_next_example
159
+ if @examples.empty?
160
+ finish
161
+ else
162
+ run_example @examples.pop
163
+ end
164
+ end
165
+
166
+ def run_example(example)
167
+ example_group = example.example_group
168
+
169
+ @reporter.example_group_started example_group
170
+ instance = example_group.new
171
+
172
+ example.run(instance, @reporter) do
173
+ example_finished example
174
+ end
175
+ end
176
+
177
+ def example_finished(example)
178
+ @reporter.example_group_finished example.example_group
179
+ run_next_example
180
+ end
181
+
182
+ # Called once all examples have finished. Just calls back to main
183
+ # runner to inform it
184
+ def finish
185
+ @finish_block.call
186
+ end
187
+ end
188
+
189
+ # An {AsyncExample} is a subclass of regular example instances which adds
190
+ # support for running an example, and waiting for a non-sync outcome. All
191
+ # async examples in a set of spec files will get registered through
192
+ # {AsyncExample.register}, and added to the {AsyncExample.examples} array
193
+ # ready for the runner to run.
194
+ #
195
+ # You will not need to create new instances of this class directly, and
196
+ # should instead use {AsyncHelpers} to create async examples.
197
+ class AsyncExample < ::RSpec::Core::Example
198
+ include AsyncHelpers
199
+
200
+ # Register new async example.
201
+ #
202
+ # @see AsyncHelpers::ClassMethods.async
203
+ def self.register(*args)
204
+ examples << new(*args)
205
+ end
206
+
207
+ # All async examples in specs.
208
+ #
209
+ # @return [Array<AsyncExample>]
210
+ def self.examples
211
+ @examples ||= []
212
+ end
213
+
214
+ def run(example_group_instance, reporter, &after_run_block)
215
+ @example_group_instance = example_group_instance
216
+ @reporter = reporter
217
+ @after_run_block = after_run_block
218
+ @finished = false
219
+
220
+ should_wait = true
221
+
222
+ ::RSpec.current_example = self
223
+ example_group_instance.instance_variable_set :@example, self
224
+
225
+ start(reporter)
226
+
227
+ begin
228
+ run_before_each
229
+ @example_group_instance.instance_exec(self, &@example_block)
230
+ rescue Exception => e
231
+ set_exception(e)
232
+ should_wait = false
233
+ end
234
+
235
+ if should_wait
236
+ delay options[:timeout] || 10 do
237
+ next if finished?
238
+
239
+ set_exception RuntimeError.new("timeout")
240
+ async_example_finished
241
+ end
242
+ else
243
+ async_example_finished
244
+ end
245
+ end
246
+
247
+ def continue_async(block)
248
+ return if finished?
249
+
250
+ begin
251
+ block.call
252
+ rescue Exception => e
253
+ set_exception(e)
254
+ end
255
+
256
+ async_example_finished
257
+ end
258
+
259
+ def finished?
260
+ @finished
261
+ end
262
+
263
+ def async_example_finished
264
+ @finished = true
265
+
266
+ begin
267
+ run_after_each
268
+ rescue Exception => e
269
+ set_exception(e)
270
+ ensure
271
+ @example_group_instance.instance_variables.each do |ivar|
272
+ @example_group_instance.instance_variable_set(ivar, nil)
273
+ end
274
+ @example_group_instance = nil
275
+
276
+ begin
277
+ assign_generated_description
278
+ rescue Exception => e
279
+ set_exception(e, "while assigning the example description")
280
+ end
281
+ end
282
+
283
+ finish(@reporter)
284
+ ::RSpec.current_example = nil
285
+ @after_run_block.call
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,188 @@
1
+ require 'erb'
2
+
3
+ module Opal
4
+ module RSpec
5
+ class BrowserFormatter < ::RSpec::Core::Formatters::BaseFormatter
6
+ include ERB::Util
7
+
8
+ CSS_STYLES = ::RSpec::Core::Formatters::HtmlPrinter::GLOBAL_STYLES
9
+
10
+ def start(example_count)
11
+ super
12
+ target = Element.new(`document.body`)
13
+ target << Element.new(:div, html: REPORT_TEMPLATE)
14
+ @rspec_results = Element.id('rspec-results')
15
+
16
+ css_text = CSS_STYLES + "\n body { padding: 0; margin: 0 }"
17
+ styles = Element.new(:style, type: 'text/css', css_text: css_text)
18
+ styles.append_to_head
19
+ end
20
+
21
+ def example_group_started(example_group)
22
+ super
23
+
24
+ @example_group_failed = false
25
+ parents = example_group.parent_groups.size
26
+
27
+ @rspec_group = Element.new(:div, class_name: "example_group passed")
28
+ @rspec_dl = Element.new(:dl)
29
+ @rspec_dt = Element.new(:dt, class_name: "passed", text: example_group.description)
30
+ @rspec_group << @rspec_dl
31
+ @rspec_dl << @rspec_dt
32
+
33
+ @rspec_dl.style 'margin-left', "#{(parents - 2) * 15}px"
34
+
35
+ @rspec_results << @rspec_group
36
+ end
37
+
38
+ def example_group_finished(example_group)
39
+ super
40
+
41
+ if @example_group_failed
42
+ @rspec_group.class_name = "example_group failed"
43
+ @rspec_dt.class_name = "failed"
44
+ Element.id('rspec-header').class_name = 'failed'
45
+ end
46
+ end
47
+
48
+ def example_failed(example)
49
+ super
50
+ duration = sprintf("%0.5f", example.execution_result[:run_time])
51
+
52
+ error = example.execution_result[:exception]
53
+ error_name = error.class.name.to_s
54
+ output = "#{short_padding}#{error_name}:\n"
55
+ error.message.to_s.split("\n").each { |line| output += "#{long_padding} #{line}\n" }
56
+
57
+ @example_group_failed = true
58
+
59
+ @rspec_dl << Element.new(:dd, class_name: "example failed", html: <<-HTML)
60
+ <span class="failed_spec_name">#{h example.description}</span>
61
+ <span class="duration">#{duration}s</span>
62
+ <div class="failure">
63
+ <div class="message"><pre>#{h output}</pre></div>
64
+ </div>
65
+ HTML
66
+ end
67
+
68
+ def example_passed(example)
69
+ super
70
+ duration = sprintf("%0.5f", example.execution_result[:run_time])
71
+
72
+ @rspec_dl << Element.new(:dd, class_name: "example passed", html: <<-HTML)
73
+ <span class="passed_spec_name">#{h example.description}</span>
74
+ <span class="duration">#{duration}s</span>
75
+ HTML
76
+ end
77
+
78
+ def dump_summary(duration, example_count, failure_count, pending_count)
79
+ super
80
+
81
+ totals = "#{example_count} examples, #{failure_count} failures"
82
+ Element.id('totals').html = totals
83
+
84
+ duration = "Finished in <strong>#{sprintf("%.5f", duration)} seconds</strong>"
85
+ Element.id('duration').html = duration
86
+
87
+ add_scripts
88
+ end
89
+
90
+ def add_scripts
91
+ content = ::RSpec::Core::Formatters::HtmlPrinter::GLOBAL_SCRIPTS
92
+ `window.eval(#{content})`
93
+ end
94
+
95
+ def short_padding
96
+ ' '
97
+ end
98
+
99
+ def long_padding
100
+ ' '
101
+ end
102
+
103
+ class Element
104
+ attr_reader :native
105
+
106
+ def self.id(id)
107
+ new(`document.getElementById(id)`)
108
+ end
109
+
110
+ def initialize(el, attrs={})
111
+ if String === el
112
+ @native = `document.createElement(el)`
113
+ else
114
+ @native = el
115
+ end
116
+
117
+ attrs.each { |name, val| __send__ "#{name}=", val }
118
+ end
119
+
120
+ def class_name=(name)
121
+ `#@native.className = #{name}`
122
+ end
123
+
124
+ def html=(html)
125
+ `#@native.innerHTML = #{html}`
126
+ end
127
+
128
+ def text=(text)
129
+ self.html = text.gsub(/</, '&lt').gsub(/>/, '&gt')
130
+ end
131
+
132
+ def type=(type)
133
+ `#@native.type = #{type}`
134
+ end
135
+
136
+ def append(child)
137
+ `#@native.appendChild(#{child.native})`
138
+ end
139
+
140
+ alias << append
141
+
142
+ def css_text=(text)
143
+ %x{
144
+ if (#@native.styleSheet) {
145
+ #@native.styleSheet.cssText = #{text};
146
+ }
147
+ else {
148
+ #@native.appendChild(document.createTextNode(#{text}));
149
+ }
150
+ }
151
+ end
152
+
153
+ def style(name, value)
154
+ `#@native.style[#{name}] = value`
155
+ end
156
+
157
+ def append_to_head
158
+ `document.getElementsByTagName('head')[0].appendChild(#@native)`
159
+ end
160
+ end
161
+
162
+ REPORT_TEMPLATE = <<-EOF
163
+ <div class="rspec-report">
164
+
165
+ <div id="rspec-header">
166
+ <div id="label">
167
+ <h1>RSpec Code Examples</h1>
168
+ </div>
169
+
170
+ <div id="display-filters">
171
+ <input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> <label for="passed_checkbox">Passed</label>
172
+ <input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> <label for="failed_checkbox">Failed</label>
173
+ <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> <label for="pending_checkbox">Pending</label>
174
+ </div>
175
+
176
+ <div id="summary">
177
+ <p id="totals">&#160;</p>
178
+ <p id="duration">&#160;</p>
179
+ </div>
180
+ </div>
181
+
182
+ <div id="rspec-results" class="results">
183
+ </div>
184
+ </div>
185
+ EOF
186
+ end
187
+ end
188
+ end