rspec-rails 6.1.2 → 7.1.1
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +272 -195
- data/README.md +28 -29
- data/lib/generators/rspec/install/templates/spec/rails_helper.rb +17 -10
- data/lib/generators/rspec/mailer/templates/preview.rb +2 -2
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +0 -15
- data/lib/generators/rspec/scaffold/templates/index_spec.rb +1 -1
- data/lib/generators/rspec/scaffold/templates/request_spec.rb +0 -15
- data/lib/rspec/rails/example/rails_example_group.rb +5 -7
- data/lib/rspec/rails/example/system_example_group.rb +9 -1
- data/lib/rspec/rails/fixture_support.rb +1 -2
- data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +10 -3
- data/lib/rspec/rails/matchers/action_cable.rb +6 -1
- data/lib/rspec/rails/matchers/active_job.rb +69 -8
- data/lib/rspec/rails/matchers/have_enqueued_mail.rb +37 -5
- data/lib/rspec/rails/matchers/have_http_status.rb +3 -7
- data/lib/rspec/rails/tasks/rspec.rake +3 -1
- data/lib/rspec/rails/version.rb +1 -1
- data/lib/rspec-rails.rb +17 -0
- data.tar.gz.sig +0 -0
- metadata +30 -35
- metadata.gz.sig +0 -0
- data/lib/generators/rspec/integration/integration_generator.rb +0 -29
data/README.md
CHANGED
@@ -9,7 +9,8 @@ detailed explanations of how the application is supposed to behave,
|
|
9
9
|
expressed in plain English.
|
10
10
|
|
11
11
|
According to [RSpec Rails new versioning strategy][] use:
|
12
|
-
* **[`rspec-rails`
|
12
|
+
* **[`rspec-rails` 7.x][]** for Rails 7.x.
|
13
|
+
* **[`rspec-rails` 6.x][]** for Rails 6.1, 7.0 or 7.1.
|
13
14
|
* **[`rspec-rails` 5.x][]** for Rails 5.2 or 6.x.
|
14
15
|
* **[`rspec-rails` 4.x][]** for Rails from 5.x or 6.x.
|
15
16
|
* **[`rspec-rails` 3.x][]** for Rails earlier than 5.0.
|
@@ -26,11 +27,12 @@ According to [RSpec Rails new versioning strategy][] use:
|
|
26
27
|
[`rspec-rails` 4.x]: https://github.com/rspec/rspec-rails/tree/4-1-maintenance
|
27
28
|
[`rspec-rails` 5.x]: https://github.com/rspec/rspec-rails/tree/5-1-maintenance
|
28
29
|
[`rspec-rails` 6.x]: https://github.com/rspec/rspec-rails/tree/6-1-maintenance
|
30
|
+
[`rspec-rails` 7.x]: https://github.com/rspec/rspec-rails/tree/7-1-maintenance
|
29
31
|
[RSpec Rails new versioning strategy]: https://github.com/rspec/rspec-rails/blob/main/rfcs/versioning-strategy.md
|
30
32
|
|
31
33
|
## Installation
|
32
34
|
|
33
|
-
**IMPORTANT** This README / branch refers to the
|
35
|
+
**IMPORTANT** This README / branch refers to the 7.1.x stable release series, only bugfixes from this series will
|
34
36
|
be added here. See the [`main` branch on Github](https://github.com/rspec/rspec-rails/tree/main) if you want or
|
35
37
|
require the latest unstable features.
|
36
38
|
|
@@ -40,15 +42,12 @@ require the latest unstable features.
|
|
40
42
|
```ruby
|
41
43
|
# Run against this stable release
|
42
44
|
group :development, :test do
|
43
|
-
gem 'rspec-rails', '~>
|
45
|
+
gem 'rspec-rails', '~> 7.0.0'
|
44
46
|
end
|
45
47
|
|
46
48
|
# Or, run against the main branch
|
47
|
-
# (requires main-branch versions of all related RSpec libraries)
|
48
49
|
group :development, :test do
|
49
|
-
|
50
|
-
gem lib, git: "https://github.com/rspec/#{lib}.git", branch: 'main'
|
51
|
-
end
|
50
|
+
gem 'rspec-rails', git: 'https://github.com/rspec/rspec-rails'
|
52
51
|
end
|
53
52
|
```
|
54
53
|
|
@@ -87,7 +86,7 @@ read the [`rspec-rails` upgrade notes][] to find out what to watch out for.
|
|
87
86
|
|
88
87
|
Be sure to check the general [RSpec upgrade notes][] as well.
|
89
88
|
|
90
|
-
[`rspec-rails` upgrade notes]: https://rspec.info/features/
|
89
|
+
[`rspec-rails` upgrade notes]: https://rspec.info/features/7-1/rspec-rails/upgrade
|
91
90
|
[RSpec upgrade notes]: https://rspec.info/upgrading-from-rspec-2/
|
92
91
|
|
93
92
|
## Usage
|
@@ -209,22 +208,22 @@ to test the various parts of a Rails system:
|
|
209
208
|
Follow the links above for examples of how each matcher is used.
|
210
209
|
|
211
210
|
[the matchers that come standard in RSpec]: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers
|
212
|
-
[`be_a_new`]: https://rspec.info/features/
|
213
|
-
[`render_template`]: https://rspec.info/features/
|
214
|
-
[`redirect_to`]: https://rspec.info/features/
|
215
|
-
[`route_to`]: https://rspec.info/features/
|
216
|
-
[`be_routable`]: https://rspec.info/features/
|
217
|
-
[`have_http_status`]: https://rspec.info/features/
|
218
|
-
[`match_array`]: https://rspec.info/features/
|
219
|
-
[`have_been_enqueued`]: https://rspec.info/features/
|
220
|
-
[`have_enqueued_job`]: https://rspec.info/features/
|
211
|
+
[`be_a_new`]: https://rspec.info/features/7-1/rspec-rails/matchers/new-record-matcher
|
212
|
+
[`render_template`]: https://rspec.info/features/7-1/rspec-rails/matchers/render-template-matcher
|
213
|
+
[`redirect_to`]: https://rspec.info/features/7-1/rspec-rails/matchers/redirect-to-matcher
|
214
|
+
[`route_to`]: https://rspec.info/features/7-1/rspec-rails/routing-specs/route-to-matcher
|
215
|
+
[`be_routable`]: https://rspec.info/features/7-1/rspec-rails/routing-specs/be-routable-matcher
|
216
|
+
[`have_http_status`]: https://rspec.info/features/7-1/rspec-rails/matchers/have-http-status-matcher
|
217
|
+
[`match_array`]: https://rspec.info/features/7-1/rspec-rails/matchers/relation-match-array
|
218
|
+
[`have_been_enqueued`]: https://rspec.info/features/7-1/rspec-rails/matchers/have-been-enqueued-matcher
|
219
|
+
[`have_enqueued_job`]: https://rspec.info/features/7-1/rspec-rails/matchers/have-enqueued-job-matcher
|
221
220
|
|
222
221
|
### What else does RSpec Rails add?
|
223
222
|
|
224
223
|
For a comprehensive look at RSpec Rails’ features,
|
225
224
|
read the [official Cucumber documentation][].
|
226
225
|
|
227
|
-
[official Cucumber documentation]: https://rspec.info/features/
|
226
|
+
[official Cucumber documentation]: https://rspec.info/features/7-1/rspec-rails
|
228
227
|
|
229
228
|
## What tests should I write?
|
230
229
|
|
@@ -269,20 +268,20 @@ RSpec.describe User, type: :model do
|
|
269
268
|
...
|
270
269
|
```
|
271
270
|
|
272
|
-
[request]: https://rspec.info/features/
|
273
|
-
[feature]: https://rspec.info/features/
|
274
|
-
[system]: https://rspec.info/features/
|
275
|
-
[model]: https://rspec.info/features/
|
276
|
-
[controller]: https://rspec.info/features/
|
277
|
-
[mailer]: https://rspec.info/features/
|
278
|
-
[job]: https://rspec.info/features/
|
279
|
-
[view]: https://rspec.info/features/
|
280
|
-
[routing]: https://rspec.info/features/
|
281
|
-
[helper]: https://rspec.info/features/
|
271
|
+
[request]: https://rspec.info/features/7-1/rspec-rails/request-specs/request-spec
|
272
|
+
[feature]: https://rspec.info/features/7-1/rspec-rails/feature-specs/feature-spec
|
273
|
+
[system]: https://rspec.info/features/7-1/rspec-rails/system-specs/system-specs
|
274
|
+
[model]: https://rspec.info/features/7-1/rspec-rails/model-specs
|
275
|
+
[controller]: https://rspec.info/features/7-1/rspec-rails/controller-specs
|
276
|
+
[mailer]: https://rspec.info/features/7-1/rspec-rails/mailer-specs
|
277
|
+
[job]: https://rspec.info/features/7-1/rspec-rails/job-specs/job-spec
|
278
|
+
[view]: https://rspec.info/features/7-1/rspec-rails/view-specs/view-spec
|
279
|
+
[routing]: https://rspec.info/features/7-1/rspec-rails/routing-specs
|
280
|
+
[helper]: https://rspec.info/features/7-1/rspec-rails/helper-specs/helper-spec
|
282
281
|
[`ActionDispatch::IntegrationTest`]: https://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html
|
283
282
|
[`ActionDispatch::SystemTestCase`]: https://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html
|
284
283
|
[`ActionController::TestCase`]: https://api.rubyonrails.org/classes/ActionController/TestCase.html
|
285
|
-
[in the appropriate folder]: https://rspec.info/features/
|
284
|
+
[in the appropriate folder]: https://rspec.info/features/7-1/rspec-rails/directory-structure
|
286
285
|
|
287
286
|
### System specs, feature specs, request specs–what’s the difference?
|
288
287
|
|
@@ -4,6 +4,11 @@ ENV['RAILS_ENV'] ||= 'test'
|
|
4
4
|
require_relative '../config/environment'
|
5
5
|
# Prevent database truncation if the environment is production
|
6
6
|
abort("The Rails environment is running in production mode!") if Rails.env.production?
|
7
|
+
<% if RSpec::Rails::FeatureCheck.has_active_record_migration? -%>
|
8
|
+
# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file
|
9
|
+
# that will avoid rails generators crashing because migrations haven't been run yet
|
10
|
+
# return unless Rails.env.test?
|
11
|
+
<% end -%>
|
7
12
|
require 'rspec/rails'
|
8
13
|
# Add additional requires below this line. Rails is not loaded until this point!
|
9
14
|
|
@@ -20,7 +25,7 @@ require 'rspec/rails'
|
|
20
25
|
# directory. Alternatively, in the individual `*_spec.rb` files, manually
|
21
26
|
# require only the support files necessary.
|
22
27
|
#
|
23
|
-
# Rails.root.glob('spec/support/**/*.rb').
|
28
|
+
# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f }
|
24
29
|
|
25
30
|
<% if RSpec::Rails::FeatureCheck.has_active_record_migration? -%>
|
26
31
|
# Checks for pending migrations and applies them before tests are run.
|
@@ -62,20 +67,22 @@ RSpec.configure do |config|
|
|
62
67
|
# config.use_transactional_fixtures = true
|
63
68
|
|
64
69
|
<% end -%>
|
65
|
-
# RSpec Rails
|
66
|
-
#
|
67
|
-
# `post` in specs under `spec/controllers`.
|
68
|
-
#
|
69
|
-
# You can disable this behaviour by removing the line below, and instead
|
70
|
-
# explicitly tag your specs with their type, e.g.:
|
70
|
+
# RSpec Rails uses metadata to mix in different behaviours to your tests,
|
71
|
+
# for example enabling you to call `get` and `post` in request specs. e.g.:
|
71
72
|
#
|
72
|
-
# RSpec.describe UsersController, type: :
|
73
|
+
# RSpec.describe UsersController, type: :request do
|
73
74
|
# # ...
|
74
75
|
# end
|
75
76
|
#
|
76
77
|
# The different available types are documented in the features, such as in
|
77
|
-
# https://rspec.info/features/
|
78
|
-
|
78
|
+
# https://rspec.info/features/7-1/rspec-rails
|
79
|
+
#
|
80
|
+
# You can also this infer these behaviours automatically by location, e.g.
|
81
|
+
# /spec/models would pull in the same behaviour as `type: :model` but this
|
82
|
+
# behaviour is considered legacy and will be removed in a future version.
|
83
|
+
#
|
84
|
+
# To enable this behaviour uncomment the line below.
|
85
|
+
# config.infer_spec_type_from_file_location!
|
79
86
|
|
80
87
|
# Filter lines from Rails gems in backtraces.
|
81
88
|
config.filter_rails_from_backtrace!
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<% module_namespacing do -%>
|
2
|
-
# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>
|
2
|
+
# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer
|
3
3
|
class <%= class_name %><%= 'Mailer' unless class_name.end_with?('Mailer') %>Preview < ActionMailer::Preview
|
4
4
|
<% actions.each do |action| -%>
|
5
5
|
|
6
|
-
# Preview this email at http://localhost:3000/rails/mailers/<%= file_path
|
6
|
+
# Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>_mailer/<%= action %>
|
7
7
|
def <%= action %>
|
8
8
|
<%= class_name.sub(/(Mailer)?$/, 'Mailer') %>.<%= action %>
|
9
9
|
end
|
@@ -90,17 +90,10 @@ RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:control
|
|
90
90
|
end
|
91
91
|
|
92
92
|
context "with invalid params" do
|
93
|
-
<% if Rails.version.to_f < 7.0 %>
|
94
|
-
it "returns a success response (i.e. to display the 'new' template)" do
|
95
|
-
post :create, params: {<%= singular_table_name %>: invalid_attributes}, session: valid_session
|
96
|
-
expect(response).to be_successful
|
97
|
-
end
|
98
|
-
<% else %>
|
99
93
|
it "renders a response with 422 status (i.e. to display the 'new' template)" do
|
100
94
|
post :create, params: {<%= singular_table_name %>: invalid_attributes}, session: valid_session
|
101
95
|
expect(response).to have_http_status(:unprocessable_entity)
|
102
96
|
end
|
103
|
-
<% end %>
|
104
97
|
end
|
105
98
|
end
|
106
99
|
|
@@ -125,19 +118,11 @@ RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:control
|
|
125
118
|
end
|
126
119
|
|
127
120
|
context "with invalid params" do
|
128
|
-
<% if Rails.version.to_f < 7.0 %>
|
129
|
-
it "returns a success response (i.e. to display the 'edit' template)" do
|
130
|
-
<%= file_name %> = <%= class_name %>.create! valid_attributes
|
131
|
-
put :update, params: {id: <%= file_name %>.to_param, <%= singular_table_name %>: invalid_attributes}, session: valid_session
|
132
|
-
expect(response).to be_successful
|
133
|
-
end
|
134
|
-
<% else %>
|
135
121
|
it "renders a response with 422 status (i.e. to display the 'edit' template)" do
|
136
122
|
<%= file_name %> = <%= class_name %>.create! valid_attributes
|
137
123
|
put :update, params: {id: <%= file_name %>.to_param, <%= singular_table_name %>: invalid_attributes}, session: valid_session
|
138
124
|
expect(response).to have_http_status(:unprocessable_entity)
|
139
125
|
end
|
140
|
-
<% end %>
|
141
126
|
end
|
142
127
|
end
|
143
128
|
|
@@ -18,7 +18,7 @@ RSpec.describe "<%= ns_table_name %>/index", <%= type_metatag(:view) %> do
|
|
18
18
|
|
19
19
|
it "renders a list of <%= ns_table_name %>" do
|
20
20
|
render
|
21
|
-
cell_selector =
|
21
|
+
cell_selector = 'div>p'
|
22
22
|
<% for attribute in output_attributes -%>
|
23
23
|
assert_select cell_selector, text: Regexp.new(<%= value_for(attribute) %>.to_s), count: 2
|
24
24
|
<% end -%>
|
@@ -83,17 +83,10 @@ RSpec.describe "/<%= name.underscore.pluralize %>", <%= type_metatag(:request) %
|
|
83
83
|
}.to change(<%= class_name %>, :count).by(0)
|
84
84
|
end
|
85
85
|
|
86
|
-
<% if Rails.version.to_f < 7.0 %>
|
87
|
-
it "renders a successful response (i.e. to display the 'new' template)" do
|
88
|
-
post <%= index_helper %>_url, params: { <%= singular_table_name %>: invalid_attributes }
|
89
|
-
expect(response).to be_successful
|
90
|
-
end
|
91
|
-
<% else %>
|
92
86
|
it "renders a response with 422 status (i.e. to display the 'new' template)" do
|
93
87
|
post <%= index_helper %>_url, params: { <%= singular_table_name %>: invalid_attributes }
|
94
88
|
expect(response).to have_http_status(:unprocessable_entity)
|
95
89
|
end
|
96
|
-
<% end %>
|
97
90
|
end
|
98
91
|
end
|
99
92
|
|
@@ -119,19 +112,11 @@ RSpec.describe "/<%= name.underscore.pluralize %>", <%= type_metatag(:request) %
|
|
119
112
|
end
|
120
113
|
|
121
114
|
context "with invalid parameters" do
|
122
|
-
<% if Rails.version.to_f < 7.0 %>
|
123
|
-
it "renders a successful response (i.e. to display the 'edit' template)" do
|
124
|
-
<%= file_name %> = <%= class_name %>.create! valid_attributes
|
125
|
-
patch <%= show_helper %>, params: { <%= singular_table_name %>: invalid_attributes }
|
126
|
-
expect(response).to be_successful
|
127
|
-
end
|
128
|
-
<% else %>
|
129
115
|
it "renders a response with 422 status (i.e. to display the 'edit' template)" do
|
130
116
|
<%= file_name %> = <%= class_name %>.create! valid_attributes
|
131
117
|
patch <%= show_helper %>, params: { <%= singular_table_name %>: invalid_attributes }
|
132
118
|
expect(response).to have_http_status(:unprocessable_entity)
|
133
119
|
end
|
134
|
-
<% end %>
|
135
120
|
end
|
136
121
|
end
|
137
122
|
|
@@ -2,9 +2,8 @@
|
|
2
2
|
# suite and ammeter.
|
3
3
|
require 'rspec/rails/matchers'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
5
|
+
require 'active_support/current_attributes/test_helper'
|
6
|
+
require 'active_support/execution_context/test_helper'
|
8
7
|
|
9
8
|
module RSpec
|
10
9
|
module Rails
|
@@ -16,10 +15,9 @@ module RSpec
|
|
16
15
|
include RSpec::Rails::MinitestLifecycleAdapter
|
17
16
|
include RSpec::Rails::MinitestAssertionAdapter
|
18
17
|
include RSpec::Rails::FixtureSupport
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
18
|
+
include RSpec::Rails::TaggedLoggingAdapter
|
19
|
+
include ActiveSupport::CurrentAttributes::TestHelper
|
20
|
+
include ActiveSupport::ExecutionContext::TestHelper
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -95,6 +95,14 @@ module RSpec
|
|
95
95
|
::Rails.application
|
96
96
|
end
|
97
97
|
|
98
|
+
# Default driver to assign if none specified.
|
99
|
+
DEFAULT_DRIVER =
|
100
|
+
if ::Rails::VERSION::STRING.to_f >= 7.2
|
101
|
+
:selenium_chrome_headless
|
102
|
+
else
|
103
|
+
:selenium
|
104
|
+
end
|
105
|
+
|
98
106
|
included do |other|
|
99
107
|
ActiveSupport.on_load(:action_dispatch_system_test_case) do
|
100
108
|
ActionDispatch::SystemTesting::Server.silence_puma = true
|
@@ -137,7 +145,7 @@ module RSpec
|
|
137
145
|
self.class.before do
|
138
146
|
# A user may have already set the driver, so only default if driver
|
139
147
|
# is not set
|
140
|
-
driven_by(
|
148
|
+
driven_by(DEFAULT_DRIVER) unless @driver
|
141
149
|
end
|
142
150
|
end
|
143
151
|
|
@@ -10,8 +10,7 @@ module RSpec
|
|
10
10
|
include ActiveRecord::TestFixtures
|
11
11
|
|
12
12
|
# @private prevent ActiveSupport::TestFixtures to start a DB transaction.
|
13
|
-
# Monkey patched to avoid collisions with 'let(:name)'
|
14
|
-
# and let(:method_name) before Rails 6.1.
|
13
|
+
# Monkey patched to avoid collisions with 'let(:name)' since Rails 6.1
|
15
14
|
def run_in_transaction?
|
16
15
|
current_example_name = (RSpec.current_example && RSpec.current_example.metadata[:description])
|
17
16
|
use_transactional_tests && !self.class.uses_transaction?(current_example_name)
|
@@ -51,6 +51,10 @@ module RSpec
|
|
51
51
|
exactly(:thrice)
|
52
52
|
end
|
53
53
|
|
54
|
+
def description
|
55
|
+
"have broadcasted #{base_description}"
|
56
|
+
end
|
57
|
+
|
54
58
|
def failure_message
|
55
59
|
"expected to broadcast #{base_message}".tap do |msg|
|
56
60
|
if @unmatching_msgs.any?
|
@@ -140,18 +144,21 @@ module RSpec
|
|
140
144
|
end
|
141
145
|
end
|
142
146
|
|
143
|
-
def
|
147
|
+
def base_description
|
144
148
|
"#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
|
145
149
|
msg << " with #{data_description(@data)}" unless @data.nil?
|
146
|
-
msg << ", but broadcast #{@matching_msgs_count}"
|
147
150
|
end
|
148
151
|
end
|
149
152
|
|
153
|
+
def base_message
|
154
|
+
"#{base_description}, but broadcast #{@matching_msgs_count}"
|
155
|
+
end
|
156
|
+
|
150
157
|
def data_description(data)
|
151
158
|
if data.is_a?(RSpec::Matchers::Composable)
|
152
159
|
data.description
|
153
160
|
else
|
154
|
-
data
|
161
|
+
data.inspect
|
155
162
|
end
|
156
163
|
end
|
157
164
|
|
@@ -3,6 +3,8 @@ require "rspec/rails/matchers/action_cable/have_broadcasted_to"
|
|
3
3
|
module RSpec
|
4
4
|
module Rails
|
5
5
|
module Matchers
|
6
|
+
extend RSpec::Matchers::DSL
|
7
|
+
|
6
8
|
# Namespace for various implementations of ActionCable features
|
7
9
|
#
|
8
10
|
# @api private
|
@@ -50,7 +52,10 @@ module RSpec
|
|
50
52
|
|
51
53
|
ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
|
52
54
|
end
|
53
|
-
|
55
|
+
|
56
|
+
alias_matcher :broadcast_to, :have_broadcasted_to do |desc|
|
57
|
+
desc.gsub("have broadcasted", "broadcast")
|
58
|
+
end
|
54
59
|
|
55
60
|
private
|
56
61
|
|
@@ -14,6 +14,7 @@ module RSpec
|
|
14
14
|
def initialize
|
15
15
|
@args = []
|
16
16
|
@queue = nil
|
17
|
+
@priority = nil
|
17
18
|
@at = nil
|
18
19
|
@block = proc { }
|
19
20
|
set_expected_number(:exactly, 1)
|
@@ -30,6 +31,11 @@ module RSpec
|
|
30
31
|
self
|
31
32
|
end
|
32
33
|
|
34
|
+
def at_priority(priority)
|
35
|
+
@priority = priority.to_i
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
33
39
|
def at(time_or_date)
|
34
40
|
case time_or_date
|
35
41
|
when Time then @at = Time.at(time_or_date.to_f)
|
@@ -71,6 +77,8 @@ module RSpec
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def failure_message
|
80
|
+
return @failure_message if defined?(@failure_message)
|
81
|
+
|
74
82
|
"expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
|
75
83
|
if @unmatching_jobs.any?
|
76
84
|
msg << "\nQueued jobs:"
|
@@ -101,7 +109,7 @@ module RSpec
|
|
101
109
|
|
102
110
|
def check(jobs)
|
103
111
|
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
|
104
|
-
if
|
112
|
+
if matches_constraints?(job)
|
105
113
|
args = deserialize_arguments(job)
|
106
114
|
@block.call(*args)
|
107
115
|
true
|
@@ -109,6 +117,12 @@ module RSpec
|
|
109
117
|
false
|
110
118
|
end
|
111
119
|
end
|
120
|
+
|
121
|
+
if (signature_mismatch = detect_args_signature_mismatch(@matching_jobs))
|
122
|
+
@failure_message = signature_mismatch
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
112
126
|
@matching_jobs_count = @matching_jobs.size
|
113
127
|
|
114
128
|
case @expectation_type
|
@@ -123,6 +137,7 @@ module RSpec
|
|
123
137
|
msg << " with #{@args}," if @args.any?
|
124
138
|
msg << " on queue #{@queue}," if @queue
|
125
139
|
msg << " at #{@at.inspect}," if @at
|
140
|
+
msg << " with priority #{@priority}," if @priority
|
126
141
|
msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
|
127
142
|
end
|
128
143
|
end
|
@@ -132,13 +147,23 @@ module RSpec
|
|
132
147
|
msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
|
133
148
|
msg_parts << "on queue #{job[:queue]}" if job[:queue]
|
134
149
|
msg_parts << "at #{Time.at(job[:at])}" if job[:at]
|
150
|
+
msg_parts <<
|
151
|
+
if job[:priority]
|
152
|
+
"with priority #{job[:priority]}"
|
153
|
+
else
|
154
|
+
"with no priority specified"
|
155
|
+
end
|
135
156
|
|
136
157
|
"#{job[:job].name} job".tap do |msg|
|
137
158
|
msg << " #{msg_parts.join(', ')}" if msg_parts.any?
|
138
159
|
end
|
139
160
|
end
|
140
161
|
|
141
|
-
def
|
162
|
+
def matches_constraints?(job)
|
163
|
+
job_matches?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job) && priority_match?(job)
|
164
|
+
end
|
165
|
+
|
166
|
+
def job_matches?(job)
|
142
167
|
@job ? @job == job[:job] : true
|
143
168
|
end
|
144
169
|
|
@@ -152,12 +177,48 @@ module RSpec
|
|
152
177
|
end
|
153
178
|
end
|
154
179
|
|
180
|
+
def detect_args_signature_mismatch(jobs)
|
181
|
+
return if skip_signature_verification?
|
182
|
+
|
183
|
+
jobs.each do |job|
|
184
|
+
args = deserialize_arguments(job)
|
185
|
+
|
186
|
+
if (signature_mismatch = check_args_signature_mismatch(job.fetch(:job), :perform, args))
|
187
|
+
return signature_mismatch
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
|
194
|
+
def skip_signature_verification?
|
195
|
+
return true unless defined?(::RSpec::Mocks) && (::RSpec::Mocks.respond_to?(:configuration))
|
196
|
+
|
197
|
+
!RSpec::Mocks.configuration.verify_partial_doubles? ||
|
198
|
+
RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification
|
199
|
+
end
|
200
|
+
|
201
|
+
def check_args_signature_mismatch(job_class, job_method, args)
|
202
|
+
signature = Support::MethodSignature.new(job_class.public_instance_method(job_method))
|
203
|
+
verifier = Support::StrictSignatureVerifier.new(signature, args)
|
204
|
+
|
205
|
+
unless verifier.valid?
|
206
|
+
"Incorrect arguments passed to #{job_class.name}: #{verifier.error_message}"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
155
210
|
def queue_match?(job)
|
156
211
|
return true unless @queue
|
157
212
|
|
158
213
|
@queue == job[:queue]
|
159
214
|
end
|
160
215
|
|
216
|
+
def priority_match?(job)
|
217
|
+
return true unless @priority
|
218
|
+
|
219
|
+
@priority == job[:priority]
|
220
|
+
end
|
221
|
+
|
161
222
|
def at_match?(job)
|
162
223
|
return true unless @at
|
163
224
|
return job[:at].nil? if @at == :no_wait
|
@@ -384,33 +445,33 @@ module RSpec
|
|
384
445
|
#
|
385
446
|
# @example
|
386
447
|
# expect {
|
387
|
-
#
|
448
|
+
# perform_enqueued_jobs { HeavyLiftingJob.perform_later }
|
388
449
|
# }.to have_performed_job
|
389
450
|
#
|
390
451
|
# expect {
|
391
|
-
#
|
452
|
+
# perform_enqueued_jobs {
|
392
453
|
# HelloJob.perform_later
|
393
454
|
# HeavyLiftingJob.perform_later
|
394
455
|
# }
|
395
456
|
# }.to have_performed_job(HelloJob).exactly(:once)
|
396
457
|
#
|
397
458
|
# expect {
|
398
|
-
#
|
459
|
+
# perform_enqueued_jobs { 3.times { HelloJob.perform_later } }
|
399
460
|
# }.to have_performed_job(HelloJob).at_least(2).times
|
400
461
|
#
|
401
462
|
# expect {
|
402
|
-
#
|
463
|
+
# perform_enqueued_jobs { HelloJob.perform_later }
|
403
464
|
# }.to have_performed_job(HelloJob).at_most(:twice)
|
404
465
|
#
|
405
466
|
# expect {
|
406
|
-
#
|
467
|
+
# perform_enqueued_jobs {
|
407
468
|
# HelloJob.perform_later
|
408
469
|
# HeavyLiftingJob.perform_later
|
409
470
|
# }
|
410
471
|
# }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
|
411
472
|
#
|
412
473
|
# expect {
|
413
|
-
#
|
474
|
+
# perform_enqueued_jobs {
|
414
475
|
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
415
476
|
# }
|
416
477
|
# }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
|
@@ -41,6 +41,8 @@ module RSpec
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def failure_message
|
44
|
+
return @failure_message if defined?(@failure_message)
|
45
|
+
|
44
46
|
"expected to enqueue #{base_message}".tap do |msg|
|
45
47
|
msg << "\n#{unmatching_mail_jobs_message}" if unmatching_mail_jobs.any?
|
46
48
|
end
|
@@ -70,7 +72,7 @@ module RSpec
|
|
70
72
|
@mailer_class ? @mailer_class.name : 'ActionMailer::Base'
|
71
73
|
end
|
72
74
|
|
73
|
-
def
|
75
|
+
def job_matches?(job)
|
74
76
|
legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
|
75
77
|
end
|
76
78
|
|
@@ -89,6 +91,23 @@ module RSpec
|
|
89
91
|
super(job)
|
90
92
|
end
|
91
93
|
|
94
|
+
def detect_args_signature_mismatch(jobs)
|
95
|
+
return if @method_name.nil?
|
96
|
+
return if skip_signature_verification?
|
97
|
+
|
98
|
+
mailer_class = mailer_class_name.constantize
|
99
|
+
|
100
|
+
jobs.each do |job|
|
101
|
+
mailer_args = extract_args_without_parameterized_params(job)
|
102
|
+
|
103
|
+
if (signature_mismatch = check_args_signature_mismatch(mailer_class, @method_name, mailer_args))
|
104
|
+
return signature_mismatch
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
92
111
|
def base_mailer_args
|
93
112
|
[mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
|
94
113
|
end
|
@@ -105,18 +124,18 @@ module RSpec
|
|
105
124
|
|
106
125
|
def unmatching_mail_jobs
|
107
126
|
@unmatching_jobs.select do |job|
|
108
|
-
|
127
|
+
job_matches?(job)
|
109
128
|
end
|
110
129
|
end
|
111
130
|
|
112
131
|
def unmatching_mail_jobs_message
|
113
|
-
|
132
|
+
messages = ["Queued deliveries:"]
|
114
133
|
|
115
134
|
unmatching_mail_jobs.each do |job|
|
116
|
-
|
135
|
+
messages << " #{mail_job_message(job)}"
|
117
136
|
end
|
118
137
|
|
119
|
-
|
138
|
+
messages.join("\n")
|
120
139
|
end
|
121
140
|
|
122
141
|
def mail_job_message(job)
|
@@ -157,6 +176,19 @@ module RSpec
|
|
157
176
|
end
|
158
177
|
end
|
159
178
|
|
179
|
+
def extract_args_without_parameterized_params(job)
|
180
|
+
args = deserialize_arguments(job)
|
181
|
+
mailer_args = args - base_mailer_args
|
182
|
+
|
183
|
+
if parameterized_mail?(job)
|
184
|
+
mailer_args = mailer_args[1..-1] # ignore parameterized params
|
185
|
+
elsif mailer_args.last.is_a?(Hash) && mailer_args.last.key?(:args)
|
186
|
+
mailer_args = args.last[:args]
|
187
|
+
end
|
188
|
+
|
189
|
+
mailer_args
|
190
|
+
end
|
191
|
+
|
160
192
|
def legacy_mail?(job)
|
161
193
|
RSpec::Rails::FeatureCheck.has_action_mailer_legacy_delivery_job? && job[:job] <= ActionMailer::DeliveryJob
|
162
194
|
end
|