opal-rspec-cj 0.4.4

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