opal-rspec 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,573 @@
1
+ module RSpec
2
+ module Core
3
+ # ExampleGroup and {Example} are the main structural elements of
4
+ # rspec-core. Consider this example:
5
+ #
6
+ # describe Thing do
7
+ # it "does something" do
8
+ # end
9
+ # end
10
+ #
11
+ # The object returned by `describe Thing` is a subclass of ExampleGroup.
12
+ # The object returned by `it "does something"` is an instance of Example,
13
+ # which serves as a wrapper for an instance of the ExampleGroup in which it
14
+ # is declared.
15
+ class ExampleGroup
16
+ extend Hooks
17
+
18
+ include MemoizedHelpers
19
+ include Pending
20
+ extend SharedExampleGroup
21
+
22
+ # @private
23
+ def self.world
24
+ RSpec.world
25
+ end
26
+
27
+ # @private
28
+ def self.register
29
+ world.register(self)
30
+ end
31
+
32
+ class << self
33
+ # @private
34
+ def self.delegate_to_metadata(*names)
35
+ names.each do |name|
36
+ define_method name do
37
+ metadata[:example_group][name]
38
+ end
39
+ end
40
+ end
41
+
42
+ def description
43
+ description = metadata[:example_group][:description]
44
+ RSpec.configuration.format_docstrings_block.call(description)
45
+ end
46
+
47
+ delegate_to_metadata :described_class, :file_path
48
+ alias_method :display_name, :description
49
+ # @private
50
+ alias_method :describes, :described_class
51
+
52
+ # @private
53
+ # @macro [attach] define_example_method
54
+ # @param [String] name
55
+ # @param [Hash] extra_options
56
+ # @param [Block] implementation
57
+ # @yield [Example] the example object
58
+ def self.define_example_method(name, extra_options={})
59
+ define_method(name) do |*all_args, &block|
60
+ desc, *args = *all_args
61
+ options = Metadata.build_hash_from(args)
62
+ options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
63
+ options.update(extra_options)
64
+ examples << RSpec::Core::Example.new(self, desc, options, block)
65
+ examples.last
66
+ end
67
+ end
68
+
69
+ # Defines an example within a group.
70
+ # @example
71
+ # example do
72
+ # end
73
+ #
74
+ # example "does something" do
75
+ # end
76
+ #
77
+ # example "does something", :with => 'additional metadata' do
78
+ # end
79
+ #
80
+ # example "does something" do |ex|
81
+ # # ex is the Example object that evals this block
82
+ # end
83
+ define_example_method :example
84
+ # Defines an example within a group.
85
+ # @example
86
+ define_example_method :it
87
+ # Defines an example within a group.
88
+ # This is here primarily for backward compatibility with early versions
89
+ # of RSpec which used `context` and `specify` instead of `describe` and
90
+ # `it`.
91
+ define_example_method :specify
92
+
93
+ # Shortcut to define an example with `:focus` => true
94
+ # @see example
95
+ define_example_method :focus, :focused => true, :focus => true
96
+ # Shortcut to define an example with `:focus` => true
97
+ # @see example
98
+ define_example_method :focused, :focused => true, :focus => true
99
+ # Shortcut to define an example with `:focus` => true
100
+ # @see example
101
+ define_example_method :fit, :focused => true, :focus => true
102
+
103
+ # Shortcut to define an example with :pending => true
104
+ # @see example
105
+ define_example_method :pending, :pending => true
106
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
107
+ # @see example
108
+ define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
109
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xit'
110
+ # @see example
111
+ define_example_method :xit, :pending => 'Temporarily disabled with xit'
112
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
113
+ # @see example
114
+ define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
115
+
116
+ # Works like `alias_method :name, :example` with the added benefit of
117
+ # assigning default metadata to the generated example.
118
+ #
119
+ # @note Use with caution. This extends the language used in your
120
+ # specs, but does not add any additional documentation. We use this
121
+ # in rspec to define methods like `focus` and `xit`, but we also add
122
+ # docs for those methods.
123
+ def alias_example_to name, extra={}
124
+ (class << self; self; end).define_example_method name, extra
125
+ end
126
+
127
+ # @private
128
+ # @macro [attach] define_nested_shared_group_method
129
+ #
130
+ # @see SharedExampleGroup
131
+ def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
132
+ define_method(new_name) do |name, *args, &customization_block|
133
+ group = describe("#{report_label} #{name}") do
134
+ find_and_eval_shared("examples", name, *args, &customization_block)
135
+ end
136
+ group.metadata[:shared_group_name] = name
137
+ group
138
+ end
139
+ end
140
+
141
+ # Generates a nested example group and includes the shared content
142
+ # mapped to `name` in the nested group.
143
+ define_nested_shared_group_method :it_behaves_like, "behaves like"
144
+ # Generates a nested example group and includes the shared content
145
+ # mapped to `name` in the nested group.
146
+ define_nested_shared_group_method :it_should_behave_like
147
+
148
+ # Works like `alias_method :name, :it_behaves_like` with the added
149
+ # benefit of assigning default metadata to the generated example.
150
+ #
151
+ # @note Use with caution. This extends the language used in your
152
+ # specs, but does not add any additional documentation. We use this
153
+ # in rspec to define `it_should_behave_like` (for backward
154
+ # compatibility), but we also add docs for that method.
155
+ def alias_it_behaves_like_to name, *args, &block
156
+ (class << self; self; end).define_nested_shared_group_method name, *args, &block
157
+ end
158
+ end
159
+
160
+ # Includes shared content mapped to `name` directly in the group in which
161
+ # it is declared, as opposed to `it_behaves_like`, which creates a nested
162
+ # group. If given a block, that block is also eval'd in the current context.
163
+ #
164
+ # @see SharedExampleGroup
165
+ def self.include_context(name, *args, &block)
166
+ find_and_eval_shared("context", name, *args, &block)
167
+ end
168
+
169
+ # Includes shared content mapped to `name` directly in the group in which
170
+ # it is declared, as opposed to `it_behaves_like`, which creates a nested
171
+ # group. If given a block, that block is also eval'd in the current context.
172
+ #
173
+ # @see SharedExampleGroup
174
+ def self.include_examples(name, *args, &block)
175
+ find_and_eval_shared("examples", name, *args, &block)
176
+ end
177
+
178
+ if RUBY_VERSION.to_f >= 1.9
179
+ # Warn when submitting the name of more than one example group to
180
+ # include_examples, it_behaves_like, etc.
181
+ #
182
+ # Helpful when upgrading from rspec-1 (which supported multiple shared
183
+ # groups in one call) to rspec-2 (which does not).
184
+ #
185
+ # See https://github.com/rspec/rspec-core/issues/1066 for background.
186
+ def self.warn_unexpected_args(label, name, args, shared_block)
187
+ if !args.empty? && shared_block.arity == 0
188
+ if shared_example_groups[args.first]
189
+ warn <<-WARNING
190
+ shared #{label} support#{'s' if /context/ =~ label.to_s} the name of only one example group, received #{[name, *args].inspect}
191
+ called from #{CallerFilter.first_non_rspec_line}"
192
+ WARNING
193
+ else
194
+ warn <<-WARNING
195
+ shared #{label} #{name.inspect} expected #{shared_block.arity} args, got #{args.inspect}
196
+ called from #{CallerFilter.first_non_rspec_line}"
197
+ WARNING
198
+ end
199
+ end
200
+ end
201
+ else
202
+ # no-op for Ruby < 1.9
203
+ #
204
+ # Ruby 1.8 reports lambda {}.arity == -1, so can't support this warning
205
+ # reliably
206
+ def self.warn_unexpected_args(*)
207
+ end
208
+ end
209
+
210
+ # @private
211
+ def self.find_and_eval_shared(label, name, *args, &customization_block)
212
+ raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
213
+ shared_block = shared_example_groups[name]
214
+
215
+ warn_unexpected_args(label, name, args, shared_block)
216
+
217
+ module_exec(*args, &shared_block)
218
+ module_eval(&customization_block) if customization_block
219
+ end
220
+
221
+ # @private
222
+ def self.examples
223
+ @examples ||= []
224
+ end
225
+
226
+ # @private
227
+ def self.filtered_examples
228
+ world.filtered_examples[self]
229
+ end
230
+
231
+ # @private
232
+ def self.descendant_filtered_examples
233
+ @descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
234
+ end
235
+
236
+ # The [Metadata](Metadata) object associated with this group.
237
+ # @see Metadata
238
+ def self.metadata
239
+ @metadata if defined?(@metadata)
240
+ end
241
+
242
+ # @private
243
+ # @return [Metadata] belonging to the parent of a nested {ExampleGroup}
244
+ def self.superclass_metadata
245
+ @superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
246
+ end
247
+
248
+ # Generates a subclass of this example group which inherits
249
+ # everything except the examples themselves.
250
+ #
251
+ # ## Examples
252
+ #
253
+ # describe "something" do # << This describe method is defined in
254
+ # # << RSpec::Core::DSL, included in the
255
+ # # << global namespace
256
+ # before do
257
+ # do_something_before
258
+ # end
259
+ #
260
+ # let(:thing) { Thing.new }
261
+ #
262
+ # describe "attribute (of something)" do
263
+ # # examples in the group get the before hook
264
+ # # declared above, and can access `thing`
265
+ # end
266
+ # end
267
+ #
268
+ # @see DSL#describe
269
+ def self.describe(*args, &example_group_block)
270
+ args << {} unless args.last.is_a?(Hash)
271
+ args.last.update(:example_group_block => example_group_block)
272
+
273
+ child = subclass(self, args, &example_group_block)
274
+ children << child
275
+ child
276
+ end
277
+
278
+ class << self
279
+ alias_method :context, :describe
280
+ end
281
+
282
+ # @private
283
+ def self.subclass(parent, args, &example_group_block)
284
+ subclass = Class.new(parent)
285
+ subclass.set_it_up(*args)
286
+ ExampleGroups.assign_const(subclass)
287
+ subclass.module_eval(&example_group_block) if example_group_block
288
+
289
+ # The LetDefinitions module must be included _after_ other modules
290
+ # to ensure that it takes precendence when there are name collisions.
291
+ # Thus, we delay including it until after the example group block
292
+ # has been eval'd.
293
+ MemoizedHelpers.define_helpers_on(subclass)
294
+
295
+ subclass
296
+ end
297
+
298
+ # @private
299
+ def self.children
300
+ @children ||= []
301
+ end
302
+
303
+ # @private
304
+ def self.descendants
305
+ @_descendants ||= [self] + children.inject([]) {|list, c| list + c.descendants}
306
+ end
307
+
308
+ ## @private
309
+ def self.parent_groups
310
+ @parent_groups ||= ancestors.select {|a| a < RSpec::Core::ExampleGroup}
311
+ end
312
+
313
+ # @private
314
+ def self.top_level?
315
+ @top_level ||= superclass == ExampleGroup
316
+ end
317
+
318
+ # @private
319
+ def self.ensure_example_groups_are_configured
320
+ unless defined?(@@example_groups_configured)
321
+ RSpec.configuration.configure_mock_framework
322
+ RSpec.configuration.configure_expectation_framework
323
+ @@example_groups_configured = true
324
+ end
325
+ end
326
+
327
+ # @private
328
+ def self.set_it_up(*args)
329
+ # Ruby 1.9 has a bug that can lead to infinite recursion and a
330
+ # SystemStackError if you include a module in a superclass after
331
+ # including it in a subclass: https://gist.github.com/845896
332
+ # To prevent this, we must include any modules in RSpec::Core::ExampleGroup
333
+ # before users create example groups and have a chance to include
334
+ # the same module in a subclass of RSpec::Core::ExampleGroup.
335
+ # So we need to configure example groups here.
336
+ ensure_example_groups_are_configured
337
+
338
+ symbol_description = args.shift if args.first.is_a?(Symbol)
339
+ args << Metadata.build_hash_from(args)
340
+ args.unshift(symbol_description) if symbol_description
341
+ @metadata = RSpec::Core::Metadata.new(superclass_metadata).process(*args)
342
+ @order = nil
343
+ hooks.register_globals(self, RSpec.configuration.hooks)
344
+ world.configure_group(self)
345
+ end
346
+
347
+ # @private
348
+ def self.before_all_ivars
349
+ @before_all_ivars ||= {}
350
+ end
351
+
352
+ # @private
353
+ def self.store_before_all_ivars(example_group_instance)
354
+ return if example_group_instance.instance_variables.empty?
355
+
356
+ example_group_instance.instance_variables.each { |ivar|
357
+ before_all_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
358
+ }
359
+ end
360
+
361
+ # @private
362
+ def self.assign_before_all_ivars(ivars, example_group_instance)
363
+ ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
364
+ end
365
+
366
+ # @private
367
+ def self.run_before_all_hooks(example_group_instance)
368
+ return if descendant_filtered_examples.empty?
369
+ begin
370
+ assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
371
+
372
+ AllHookMemoizedHash::Before.isolate_for_all_hook(example_group_instance) do
373
+ run_hook(:before, :all, example_group_instance)
374
+ end
375
+ ensure
376
+ store_before_all_ivars(example_group_instance)
377
+ end
378
+ end
379
+
380
+ # @private
381
+ def self.run_around_each_hooks(example, initial_procsy)
382
+ run_hook(:around, :each, example, initial_procsy)
383
+ end
384
+
385
+ # @private
386
+ def self.run_before_each_hooks(example)
387
+ run_hook(:before, :each, example)
388
+ end
389
+
390
+ # @private
391
+ def self.run_after_each_hooks(example)
392
+ run_hook(:after, :each, example)
393
+ end
394
+
395
+ # @private
396
+ def self.run_after_all_hooks(example_group_instance)
397
+ return if descendant_filtered_examples.empty?
398
+ assign_before_all_ivars(before_all_ivars, example_group_instance)
399
+
400
+ AllHookMemoizedHash::After.isolate_for_all_hook(example_group_instance) do
401
+ run_hook(:after, :all, example_group_instance)
402
+ end
403
+ end
404
+
405
+ # Runs all the examples in this group
406
+ def self.run(reporter)
407
+ if RSpec.wants_to_quit
408
+ RSpec.clear_remaining_example_groups if top_level?
409
+ return
410
+ end
411
+ reporter.example_group_started(self)
412
+
413
+ begin
414
+ run_before_all_hooks(new)
415
+ result_for_this_group = run_examples(reporter)
416
+ results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
417
+ result_for_this_group && results_for_descendants
418
+ rescue Exception => ex
419
+ RSpec.wants_to_quit = true if fail_fast?
420
+ fail_filtered_examples(ex, reporter)
421
+ ensure
422
+ run_after_all_hooks(new)
423
+ before_all_ivars.clear
424
+ reporter.example_group_finished(self)
425
+ end
426
+ end
427
+
428
+ # @private
429
+ def self.ordering_strategy
430
+ order = metadata.fetch(:order, :global)
431
+ registry = RSpec.configuration.ordering_registry
432
+
433
+ registry.fetch(order) do
434
+ warn <<-WARNING
435
+ WARN: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
436
+ WARN
437
+ WARN
438
+ WARNING
439
+
440
+ registry.fetch(:global)
441
+ end
442
+ end
443
+
444
+ # @private
445
+ def self.run_examples(reporter)
446
+ ordering_strategy.order(filtered_examples).map do |example|
447
+ next if RSpec.wants_to_quit
448
+ instance = new
449
+ set_ivars(instance, before_all_ivars)
450
+ succeeded = example.run(instance, reporter)
451
+ RSpec.wants_to_quit = true if fail_fast? && !succeeded
452
+ succeeded
453
+ end.all?
454
+ end
455
+
456
+ # @private
457
+ def self.fail_filtered_examples(exception, reporter)
458
+ filtered_examples.each { |example| example.fail_with_exception(reporter, exception) }
459
+
460
+ children.each do |child|
461
+ reporter.example_group_started(child)
462
+ child.fail_filtered_examples(exception, reporter)
463
+ reporter.example_group_finished(child)
464
+ end
465
+ false
466
+ end
467
+
468
+ # @private
469
+ def self.fail_fast?
470
+ RSpec.configuration.fail_fast?
471
+ end
472
+
473
+ # @private
474
+ def self.any_apply?(filters)
475
+ metadata.any_apply?(filters)
476
+ end
477
+
478
+ # @private
479
+ def self.all_apply?(filters)
480
+ metadata.all_apply?(filters)
481
+ end
482
+
483
+ # @private
484
+ def self.declaration_line_numbers
485
+ @declaration_line_numbers ||= [metadata[:example_group][:line_number]] +
486
+ examples.collect {|e| e.metadata[:line_number]} +
487
+ children.inject([]) {|l,c| l + c.declaration_line_numbers}
488
+ end
489
+
490
+ # @private
491
+ def self.top_level_description
492
+ parent_groups.last.description
493
+ end
494
+
495
+ # @private
496
+ def self.set_ivars(instance, ivars)
497
+ ivars.each {|name, value| instance.instance_variable_set(name, value)}
498
+ end
499
+
500
+ # Returns the class or module passed to the `describe` method (or alias).
501
+ # Returns nil if the subject is not a class or module.
502
+ # @example
503
+ # describe Thing do
504
+ # it "does something" do
505
+ # described_class == Thing
506
+ # end
507
+ # end
508
+ #
509
+ #
510
+ def described_class
511
+ self.class.described_class
512
+ end
513
+
514
+ # @private
515
+ # instance_evals the block, capturing and reporting an exception if
516
+ # raised
517
+ def instance_exec_with_rescue(example, context = nil, &hook)
518
+ begin
519
+ instance_exec(example, &hook)
520
+ rescue Exception => e
521
+ if RSpec.current_example
522
+ RSpec.current_example.set_exception(e, context)
523
+ else
524
+ raise
525
+ end
526
+ end
527
+ end
528
+ end
529
+ end
530
+
531
+ # Namespace for the example group subclasses generated by top-level `describe`.
532
+ module ExampleGroups
533
+ def self.assign_const(group)
534
+ base_name = base_name_for(group)
535
+ const_scope = constant_scope_for(group)
536
+ name = disambiguate(base_name, const_scope)
537
+
538
+ const_scope.const_set(name, group)
539
+ end
540
+
541
+ def self.constant_scope_for(group)
542
+ const_scope = group.superclass
543
+ const_scope = self if const_scope == Core::ExampleGroup
544
+ const_scope
545
+ end
546
+
547
+ def self.base_name_for(group)
548
+ return "Anonymous" if group.description.empty?
549
+
550
+ # convert to CamelCase
551
+ name = ' ' + group.description
552
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { $1.upcase }
553
+
554
+ name.lstrip! # Remove leading whitespace
555
+ name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
556
+
557
+ # Ruby requires first const letter to be A-Z. Use `Nested`
558
+ # as necessary to enforce that.
559
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
560
+
561
+ name
562
+ end
563
+
564
+ def self.disambiguate(name, const_scope)
565
+ return name unless const_scope.const_defined?(name)
566
+
567
+ # Add a trailing number if needed to disambiguate from an existing constant.
568
+ name << "_2"
569
+ name.next! while const_scope.const_defined?(name)
570
+ name
571
+ end
572
+ end
573
+ end