rspec-rails 4.0.0.beta3 → 4.0.0.beta4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Capybara.md +1 -1
  5. data/Changelog.md +48 -12
  6. data/README.md +3 -4
  7. data/lib/generators/rspec.rb +0 -6
  8. data/lib/generators/rspec/controller/controller_generator.rb +13 -5
  9. data/lib/generators/rspec/controller/templates/request_spec.rb +14 -0
  10. data/lib/generators/rspec/controller/templates/routing_spec.rb +1 -1
  11. data/lib/generators/rspec/feature/feature_generator.rb +2 -2
  12. data/lib/generators/rspec/generators/generator_generator.rb +1 -1
  13. data/lib/generators/rspec/helper/helper_generator.rb +1 -1
  14. data/lib/generators/rspec/install/install_generator.rb +4 -4
  15. data/lib/generators/rspec/install/templates/spec/rails_helper.rb +2 -15
  16. data/lib/generators/rspec/integration/integration_generator.rb +3 -3
  17. data/lib/generators/rspec/mailer/mailer_generator.rb +1 -1
  18. data/lib/generators/rspec/model/model_generator.rb +4 -4
  19. data/lib/generators/rspec/scaffold/scaffold_generator.rb +10 -10
  20. data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +0 -36
  21. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +10 -10
  22. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +1 -1
  23. data/lib/generators/rspec/scaffold/templates/index_spec.rb +2 -2
  24. data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
  25. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +8 -10
  26. data/lib/generators/rspec/scaffold/templates/show_spec.rb +1 -1
  27. data/lib/generators/rspec/system/system_generator.rb +1 -1
  28. data/lib/generators/rspec/view/view_generator.rb +2 -2
  29. data/lib/rspec-rails.rb +4 -7
  30. data/lib/rspec/rails/adapters.rb +8 -74
  31. data/lib/rspec/rails/configuration.rb +32 -32
  32. data/lib/rspec/rails/example/controller_example_group.rb +4 -4
  33. data/lib/rspec/rails/example/feature_example_group.rb +5 -7
  34. data/lib/rspec/rails/example/helper_example_group.rb +2 -10
  35. data/lib/rspec/rails/example/rails_example_group.rb +1 -1
  36. data/lib/rspec/rails/example/system_example_group.rb +6 -3
  37. data/lib/rspec/rails/example/view_example_group.rb +38 -48
  38. data/lib/rspec/rails/extensions/active_record/proxy.rb +1 -9
  39. data/lib/rspec/rails/feature_check.rb +1 -28
  40. data/lib/rspec/rails/fixture_file_upload_support.rb +1 -1
  41. data/lib/rspec/rails/fixture_support.rb +8 -13
  42. data/lib/rspec/rails/matchers/action_cable.rb +1 -1
  43. data/lib/rspec/rails/matchers/active_job.rb +124 -14
  44. data/lib/rspec/rails/matchers/base_matcher.rb +4 -10
  45. data/lib/rspec/rails/matchers/have_enqueued_mail.rb +5 -2
  46. data/lib/rspec/rails/matchers/have_http_status.rb +7 -7
  47. data/lib/rspec/rails/matchers/routing_matchers.rb +10 -10
  48. data/lib/rspec/rails/tasks/rspec.rake +3 -13
  49. data/lib/rspec/rails/vendor/capybara.rb +10 -10
  50. data/lib/rspec/rails/version.rb +1 -1
  51. data/lib/rspec/rails/view_path_builder.rb +1 -1
  52. data/lib/rspec/rails/view_rendering.rb +3 -3
  53. metadata +16 -17
  54. metadata.gz.sig +0 -0
  55. data/lib/generators/rspec/observer/observer_generator.rb +0 -13
  56. data/lib/generators/rspec/observer/templates/observer_spec.rb +0 -7
@@ -94,9 +94,9 @@ module RSpec
94
94
  self.routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
95
95
  r.draw do
96
96
  resources resource_name,
97
- :as => resource_as,
98
- :module => resource_module,
99
- :path => resource_path
97
+ as: resource_as,
98
+ module: resource_module,
99
+ path: resource_path
100
100
  end
101
101
  end
102
102
  end
@@ -159,7 +159,7 @@ module RSpec
159
159
  #
160
160
  # expect do
161
161
  # bypass_rescue
162
- # get :show, :id => profile.id + 1
162
+ # get :show, id: profile.id + 1
163
163
  # end.to raise_error(/403 Forbidden/)
164
164
  # end
165
165
  # end
@@ -15,9 +15,7 @@ module RSpec
15
15
  include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
16
16
  include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
17
17
 
18
- if respond_to?(:default_url_options)
19
- default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST
20
- end
18
+ default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST
21
19
  end
22
20
  end
23
21
 
@@ -37,9 +35,9 @@ end
37
35
 
38
36
  unless RSpec.respond_to?(:feature)
39
37
  opts = {
40
- :capybara_feature => true,
41
- :type => :feature,
42
- :skip => <<-EOT.squish
38
+ capybara_feature: true,
39
+ type: :feature,
40
+ skip: <<-EOT.squish
43
41
  Feature specs require the Capybara (https://github.com/jnicklas/capybara)
44
42
  gem, version 2.2.0 or later. We recommend version 2.4.0 or later to avoid
45
43
  some deprecation warnings and have support for
@@ -63,7 +61,7 @@ unless RSpec.respond_to?(:feature)
63
61
  main_feature = nil unless c.expose_dsl_globally?
64
62
  c.alias_example_group_to :feature, opts
65
63
  c.alias_example_to :scenario
66
- c.alias_example_to :xscenario, :skip => 'Temporarily skipped with xscenario'
64
+ c.alias_example_to :xscenario, skip: 'Temporarily skipped with xscenario'
67
65
  end
68
66
 
69
67
  # Due to load order issues and `config.expose_dsl_globally?` defaulting to
@@ -12,16 +12,8 @@ module RSpec
12
12
 
13
13
  # @private
14
14
  module ClassMethods
15
- if ::Rails::VERSION::MAJOR > 3
16
- def determine_constant_from_test_name(_ignore)
17
- described_class if yield(described_class)
18
- end
19
- else
20
- def determine_default_helper_class(_ignore)
21
- return unless Module === described_class && !(Class === described_class)
22
-
23
- described_class
24
- end
15
+ def determine_constant_from_test_name(_ignore)
16
+ described_class if yield(described_class)
25
17
  end
26
18
  end
27
19
 
@@ -9,7 +9,7 @@ module RSpec
9
9
  module RailsExampleGroup
10
10
  extend ActiveSupport::Concern
11
11
  include RSpec::Rails::SetupAndTeardownAdapter
12
- include RSpec::Rails::MinitestLifecycleAdapter if ::Rails::VERSION::STRING >= '4'
12
+ include RSpec::Rails::MinitestLifecycleAdapter
13
13
  include RSpec::Rails::MinitestAssertionAdapter
14
14
  include RSpec::Rails::FixtureSupport
15
15
  end
@@ -85,6 +85,12 @@ module RSpec
85
85
  def initialize(*args, &blk)
86
86
  super(*args, &blk)
87
87
  @driver = nil
88
+
89
+ self.class.before do
90
+ # A user may have already set the driver, so only default if driver
91
+ # is not set
92
+ driven_by(:selenium) unless @driver
93
+ end
88
94
  end
89
95
 
90
96
  def driven_by(*args, &blk)
@@ -92,9 +98,6 @@ module RSpec
92
98
  end
93
99
 
94
100
  before do
95
- # A user may have already set the driver, so only default if driver
96
- # is not set
97
- driven_by(:selenium) unless @driver
98
101
  @routes = ::Rails.application.routes
99
102
  end
100
103
 
@@ -43,16 +43,13 @@ module RSpec
43
43
 
44
44
  included do
45
45
  include ::Rails.application.routes.url_helpers
46
-
47
- if ::Rails.application.routes.respond_to?(:mounted_helpers)
48
- include ::Rails.application.routes.mounted_helpers
49
- end
46
+ include ::Rails.application.routes.mounted_helpers
50
47
  end
51
48
 
52
49
  # @overload render
53
- # @overload render({:partial => path_to_file})
54
- # @overload render({:partial => path_to_file}, {... locals ...})
55
- # @overload render({:partial => path_to_file}, {... locals ...}) do ... end
50
+ # @overload render({partial: path_to_file})
51
+ # @overload render({partial: path_to_file}, {... locals ...})
52
+ # @overload render({partial: path_to_file}, {... locals ...}) do ... end
56
53
  #
57
54
  # Delegates to ActionView::Base#render, so see documentation on that
58
55
  # for more info.
@@ -62,7 +59,7 @@ module RSpec
62
59
  #
63
60
  # describe "widgets/new.html.erb" do
64
61
  # it "shows all the widgets" do
65
- # render # => view.render(:file => "widgets/new.html.erb")
62
+ # render # => view.render(file: "widgets/new.html.erb")
66
63
  # # ...
67
64
  # end
68
65
  # end
@@ -105,7 +102,7 @@ module RSpec
105
102
 
106
103
  # @deprecated Use `view` instead.
107
104
  def template
108
- RSpec.deprecate("template", :replacement => "view")
105
+ RSpec.deprecate("template", replacement: "view")
109
106
  view
110
107
  end
111
108
 
@@ -128,41 +125,37 @@ module RSpec
128
125
  private
129
126
 
130
127
  def _default_render_options
131
- if ::Rails::VERSION::STRING >= '3.2'
132
- formats = if ActionView::Template::Types.respond_to?(:symbols)
133
- ActionView::Template::Types.symbols
134
- else
135
- [:html, :text, :js, :css, :xml, :json].map(&:to_s)
136
- end.map { |x| Regexp.escape(x) }.join("|")
137
-
138
- handlers = ActionView::Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
139
- locales = "[a-z]{2}(?:-[A-Z]{2})?"
140
- variants = "[^.]*"
141
- path_regex = %r{
142
- \A
143
- (?<template>.*?)
144
- (?:\.(?<locale>#{locales}))??
145
- (?:\.(?<format>#{formats}))??
146
- (?:\+(?<variant>#{variants}))??
147
- (?:\.(?<handler>#{handlers}))?
148
- \z
149
- }x
150
-
151
- # This regex should always find a match.
152
- # Worst case, everything will be nil, and :template will just be
153
- # the original string.
154
- match = path_regex.match(_default_file_to_render)
155
-
156
- render_options = { :template => match[:template] }
157
- render_options[:handlers] = [match[:handler]] if match[:handler]
158
- render_options[:formats] = [match[:format].to_sym] if match[:format]
159
- render_options[:locales] = [match[:locale]] if match[:locale]
160
- render_options[:variants] = [match[:variant]] if match[:variant]
161
-
162
- render_options
163
- else
164
- { :template => _default_file_to_render }
165
- end
128
+ formats = if ActionView::Template::Types.respond_to?(:symbols)
129
+ ActionView::Template::Types.symbols
130
+ else
131
+ [:html, :text, :js, :css, :xml, :json].map(&:to_s)
132
+ end.map { |x| Regexp.escape(x) }.join("|")
133
+
134
+ handlers = ActionView::Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
135
+ locales = "[a-z]{2}(?:-[A-Z]{2})?"
136
+ variants = "[^.]*"
137
+ path_regex = %r{
138
+ \A
139
+ (?<template>.*?)
140
+ (?:\.(?<locale>#{locales}))??
141
+ (?:\.(?<format>#{formats}))??
142
+ (?:\+(?<variant>#{variants}))??
143
+ (?:\.(?<handler>#{handlers}))?
144
+ \z
145
+ }x
146
+
147
+ # This regex should always find a match.
148
+ # Worst case, everything will be nil, and :template will just be
149
+ # the original string.
150
+ match = path_regex.match(_default_file_to_render)
151
+
152
+ render_options = { template: match[:template] }
153
+ render_options[:handlers] = [match[:handler]] if match[:handler]
154
+ render_options[:formats] = [match[:format].to_sym] if match[:format]
155
+ render_options[:locales] = [match[:locale]] if match[:locale]
156
+ render_options[:variants] = [match[:variant]] if match[:variant]
157
+
158
+ render_options
166
159
  end
167
160
 
168
161
  def _path_parts
@@ -192,10 +185,7 @@ module RSpec
192
185
 
193
186
  before do
194
187
  _include_controller_helpers
195
- if view.lookup_context.respond_to?(:prefixes)
196
- # rails 3.1
197
- view.lookup_context.prefixes << _controller_path
198
- end
188
+ view.lookup_context.prefixes << _controller_path
199
189
 
200
190
  controller.controller_path = _controller_path
201
191
 
@@ -2,15 +2,7 @@ RSpec.configure do |rspec|
2
2
  # Delay this in order to give users a chance to configure `expect_with`...
3
3
  rspec.before(:suite) do
4
4
  if defined?(RSpec::Matchers) && RSpec::Matchers.configuration.syntax.include?(:should) && defined?(ActiveRecord::Associations)
5
- # In Rails 3.0, it was AssociationProxy.
6
- # In 3.1+, it's CollectionProxy.
7
- const_name = [:CollectionProxy, :AssociationProxy].find do |const|
8
- ActiveRecord::Associations.const_defined?(const)
9
- end
10
-
11
- proxy_class = ActiveRecord::Associations.const_get(const_name)
12
-
13
- RSpec::Matchers.configuration.add_should_and_should_not_to proxy_class
5
+ RSpec::Matchers.configuration.add_should_and_should_not_to ActiveRecord::Associations::CollectionProxy
14
6
  end
15
7
  end
16
8
  end
@@ -3,16 +3,6 @@ module RSpec
3
3
  # @private
4
4
  module FeatureCheck
5
5
  module_function
6
- def can_check_pending_migrations?
7
- has_active_record_migration? &&
8
- ::ActiveRecord::Migration.respond_to?(:check_pending!)
9
- end
10
-
11
- def can_maintain_test_schema?
12
- has_active_record_migration? &&
13
- ::ActiveRecord::Migration.respond_to?(:maintain_test_schema!)
14
- end
15
-
16
6
  def has_active_job?
17
7
  defined?(::ActiveJob)
18
8
  end
@@ -37,11 +27,6 @@ module RSpec
37
27
  defined?(::ActionCable) && ActionCable::VERSION::MAJOR >= 6
38
28
  end
39
29
 
40
- def has_action_mailer_show_preview?
41
- has_action_mailer_preview? &&
42
- ::ActionMailer::Base.respond_to?(:show_previews=)
43
- end
44
-
45
30
  def has_action_mailer_parameterized?
46
31
  has_action_mailer? && defined?(::ActionMailer::Parameterized)
47
32
  end
@@ -54,20 +39,8 @@ module RSpec
54
39
  defined?(::ActionMailbox)
55
40
  end
56
41
 
57
- def has_1_9_hash_syntax?
58
- ::Rails::VERSION::STRING > '4.0'
59
- end
60
-
61
- def has_file_fixture?
62
- ::Rails::VERSION::STRING > '5.0'
63
- end
64
-
65
42
  def type_metatag(type)
66
- if has_1_9_hash_syntax?
67
- "type: :#{type}"
68
- else
69
- ":type => :#{type}"
70
- end
43
+ "type: :#{type}"
71
44
  end
72
45
  end
73
46
  end
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Rails
3
3
  # @private
4
4
  module FixtureFileUploadSupport
5
- delegate :fixture_file_upload, :to => :rails_fixture_file_wrapper
5
+ delegate :fixture_file_upload, to: :rails_fixture_file_wrapper
6
6
 
7
7
  private
8
8
 
@@ -5,29 +5,18 @@ module RSpec
5
5
  if defined?(ActiveRecord::TestFixtures)
6
6
  extend ActiveSupport::Concern
7
7
  include RSpec::Rails::SetupAndTeardownAdapter
8
- include RSpec::Rails::MinitestLifecycleAdapter if ::ActiveRecord::VERSION::STRING > '4'
8
+ include RSpec::Rails::MinitestLifecycleAdapter
9
9
  include RSpec::Rails::MinitestAssertionAdapter
10
10
  include ActiveRecord::TestFixtures
11
11
 
12
12
  included do
13
- # TODO: (DC 2011-06-25) this is necessary because fixture_file_upload
14
- # accesses fixture_path directly on ActiveSupport::TestCase. This is
15
- # fixed in rails by https://github.com/rails/rails/pull/1861, which
16
- # should be part of the 3.1 release, at which point we can include
17
- # these lines for rails < 3.1.
18
- ActiveSupport::TestCase.class_exec do
19
- include ActiveRecord::TestFixtures
20
- self.fixture_path = RSpec.configuration.fixture_path
21
- end
22
- # /TODO
23
-
24
13
  self.fixture_path = RSpec.configuration.fixture_path
25
14
  if ::Rails::VERSION::STRING > '5'
26
15
  self.use_transactional_tests = RSpec.configuration.use_transactional_fixtures
27
16
  else
28
17
  self.use_transactional_fixtures = RSpec.configuration.use_transactional_fixtures
29
18
  end
30
- self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures
19
+ self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures
31
20
 
32
21
  def self.fixtures(*args)
33
22
  orig_methods = private_instance_methods
@@ -50,6 +39,12 @@ module RSpec
50
39
  end
51
40
  end
52
41
 
42
+ if ::Rails.version.to_f >= 6.1
43
+ def name
44
+ @example
45
+ end
46
+ end
47
+
53
48
  fixtures RSpec.configuration.global_fixtures if RSpec.configuration.global_fixtures
54
49
  end
55
50
  end
@@ -48,7 +48,7 @@ module RSpec
48
48
  def have_broadcasted_to(target = nil)
49
49
  check_action_cable_adapter
50
50
 
51
- ActionCable::HaveBroadcastedTo.new(target, :channel => described_class)
51
+ ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
52
52
  end
53
53
  alias_method :broadcast_to, :have_broadcasted_to
54
54
 
@@ -67,7 +67,7 @@ module RSpec
67
67
  end
68
68
 
69
69
  def failure_message
70
- "expected to enqueue #{base_message}".tap do |msg|
70
+ "expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
71
71
  if @unmatching_jobs.any?
72
72
  msg << "\nQueued jobs:"
73
73
  @unmatching_jobs.each do |job|
@@ -78,7 +78,7 @@ module RSpec
78
78
  end
79
79
 
80
80
  def failure_message_when_negated
81
- "expected not to enqueue #{base_message}"
81
+ "expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
82
82
  end
83
83
 
84
84
  def message_expectation_modifier
@@ -97,7 +97,7 @@ module RSpec
97
97
 
98
98
  def check(jobs)
99
99
  @matching_jobs, @unmatching_jobs = jobs.partition do |job|
100
- if job_match?(job) && arguments_match?(job) && other_attributes_match?(job)
100
+ if job_match?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job)
101
101
  args = deserialize_arguments(job)
102
102
  @block.call(*args)
103
103
  true
@@ -119,7 +119,7 @@ module RSpec
119
119
  msg << " with #{@args}," if @args.any?
120
120
  msg << " on queue #{@queue}," if @queue
121
121
  msg << " at #{@at.inspect}," if @at
122
- msg << " but enqueued #{@matching_jobs_count}"
122
+ msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
123
123
  end
124
124
  end
125
125
 
@@ -148,19 +148,18 @@ module RSpec
148
148
  end
149
149
  end
150
150
 
151
- def other_attributes_match?(job)
152
- serialized_attributes.all? { |key, value| value == job[key] }
153
- end
151
+ def queue_match?(job)
152
+ return true unless @queue
154
153
 
155
- def serialized_attributes
156
- {}.tap do |attributes|
157
- attributes[:at] = serialized_at if @at
158
- attributes[:queue] = @queue if @queue
159
- end
154
+ @queue == job[:queue]
160
155
  end
161
156
 
162
- def serialized_at
163
- @at == :no_wait ? nil : @at.to_f
157
+ def at_match?(job)
158
+ return true unless @at
159
+ return job[:at].nil? if @at == :no_wait
160
+ return false unless job[:at]
161
+
162
+ values_match?(@at, Time.at(job[:at]))
164
163
  end
165
164
 
166
165
  def set_expected_number(relativity, count)
@@ -194,6 +193,9 @@ module RSpec
194
193
 
195
194
  # @private
196
195
  class HaveEnqueuedJob < Base
196
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
197
+ MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
198
+
197
199
  def initialize(job)
198
200
  super()
199
201
  @job = job
@@ -218,6 +220,9 @@ module RSpec
218
220
 
219
221
  # @private
220
222
  class HaveBeenEnqueued < Base
223
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
224
+ MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
225
+
221
226
  def matches?(job)
222
227
  @job = job
223
228
  check(queue_adapter.enqueued_jobs)
@@ -229,6 +234,38 @@ module RSpec
229
234
  !matches?(proc)
230
235
  end
231
236
  end
237
+
238
+ # @private
239
+ class HavePerformedJob < Base
240
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
241
+ MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
242
+
243
+ def initialize(job)
244
+ super()
245
+ @job = job
246
+ end
247
+
248
+ def matches?(proc)
249
+ raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc
250
+
251
+ original_performed_jobs_count = queue_adapter.performed_jobs.count
252
+ proc.call
253
+ in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)
254
+
255
+ check(in_block_jobs)
256
+ end
257
+ end
258
+
259
+ # @private
260
+ class HaveBeenPerformed < Base
261
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
262
+ MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
263
+
264
+ def matches?(job)
265
+ @job = job
266
+ check(queue_adapter.performed_jobs)
267
+ end
268
+ end
232
269
  end
233
270
 
234
271
  # @api public
@@ -316,6 +353,79 @@ module RSpec
316
353
  ActiveJob::HaveBeenEnqueued.new
317
354
  end
318
355
 
356
+ # @api public
357
+ # Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
358
+ #
359
+ # @example
360
+ # expect {
361
+ # perform_jobs { HeavyLiftingJob.perform_later }
362
+ # }.to have_performed_job
363
+ #
364
+ # expect {
365
+ # perform_jobs {
366
+ # HelloJob.perform_later
367
+ # HeavyLiftingJob.perform_later
368
+ # }
369
+ # }.to have_performed_job(HelloJob).exactly(:once)
370
+ #
371
+ # expect {
372
+ # perform_jobs { 3.times { HelloJob.perform_later } }
373
+ # }.to have_performed_job(HelloJob).at_least(2).times
374
+ #
375
+ # expect {
376
+ # perform_jobs { HelloJob.perform_later }
377
+ # }.to have_performed_job(HelloJob).at_most(:twice)
378
+ #
379
+ # expect {
380
+ # perform_jobs {
381
+ # HelloJob.perform_later
382
+ # HeavyLiftingJob.perform_later
383
+ # }
384
+ # }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
385
+ #
386
+ # expect {
387
+ # perform_jobs {
388
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
389
+ # }
390
+ # }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
391
+ def have_performed_job(job = nil)
392
+ check_active_job_adapter
393
+ ActiveJob::HavePerformedJob.new(job)
394
+ end
395
+ alias_method :perform_job, :have_performed_job
396
+
397
+ # @api public
398
+ # Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
399
+ #
400
+ # @example
401
+ # before do
402
+ # ActiveJob::Base.queue_adapter.performed_jobs.clear
403
+ # ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
404
+ # ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
405
+ # end
406
+ #
407
+ # HeavyLiftingJob.perform_later
408
+ # expect(HeavyLiftingJob).to have_been_performed
409
+ #
410
+ # HelloJob.perform_later
411
+ # HeavyLiftingJob.perform_later
412
+ # expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
413
+ #
414
+ # 3.times { HelloJob.perform_later }
415
+ # expect(HelloJob).to have_been_performed.at_least(2).times
416
+ #
417
+ # HelloJob.perform_later
418
+ # HeavyLiftingJob.perform_later
419
+ # expect(HelloJob).to have_been_performed
420
+ # expect(HeavyLiftingJob).to have_been_performed
421
+ #
422
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
423
+ # expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
424
+ def have_been_performed
425
+ check_active_job_adapter
426
+ ActiveJob::HaveBeenPerformed.new
427
+ end
428
+
319
429
  private
320
430
 
321
431
  # @private