rspec-rails 2.14.2 → 3.9.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +4 -2
- data/Capybara.md +2 -4
- data/Changelog.md +592 -34
- data/{License.txt → LICENSE.md} +5 -2
- data/README.md +290 -369
- data/lib/generators/rspec/controller/controller_generator.rb +1 -0
- data/lib/generators/rspec/controller/templates/controller_spec.rb +5 -5
- data/lib/generators/rspec/controller/templates/view_spec.rb +2 -2
- data/lib/generators/rspec/feature/feature_generator.rb +29 -0
- data/lib/generators/rspec/feature/templates/feature_singular_spec.rb +5 -0
- data/lib/generators/rspec/feature/templates/feature_spec.rb +5 -0
- data/lib/generators/rspec/generators/generator_generator.rb +24 -0
- data/lib/generators/rspec/generators/templates/generator_spec.rb +6 -0
- data/lib/generators/rspec/helper/helper_generator.rb +1 -0
- data/lib/generators/rspec/helper/templates/helper_spec.rb +2 -2
- data/lib/generators/rspec/install/install_generator.rb +44 -5
- data/lib/generators/rspec/install/templates/spec/rails_helper.rb +78 -0
- data/lib/generators/rspec/integration/integration_generator.rb +8 -13
- data/lib/generators/rspec/integration/templates/request_spec.rb +4 -9
- data/lib/generators/rspec/job/job_generator.rb +12 -0
- data/lib/generators/rspec/job/templates/job_spec.rb.erb +7 -0
- data/lib/generators/rspec/mailer/mailer_generator.rb +7 -0
- data/lib/generators/rspec/mailer/templates/mailer_spec.rb +7 -7
- data/lib/generators/rspec/mailer/templates/preview.rb +13 -0
- data/lib/generators/rspec/model/model_generator.rb +19 -5
- data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
- data/lib/generators/rspec/model/templates/model_spec.rb +2 -2
- data/lib/generators/rspec/observer/observer_generator.rb +1 -0
- data/lib/generators/rspec/observer/templates/observer_spec.rb +2 -2
- data/lib/generators/rspec/request/request_generator.rb +10 -0
- data/lib/generators/rspec/scaffold/scaffold_generator.rb +68 -138
- data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +165 -0
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +98 -73
- data/lib/generators/rspec/scaffold/templates/edit_spec.rb +9 -13
- data/lib/generators/rspec/scaffold/templates/index_spec.rb +3 -10
- data/lib/generators/rspec/scaffold/templates/new_spec.rb +10 -14
- data/lib/generators/rspec/scaffold/templates/routing_spec.rb +21 -12
- data/lib/generators/rspec/scaffold/templates/show_spec.rb +4 -11
- data/lib/generators/rspec/system/system_generator.rb +26 -0
- data/lib/generators/rspec/system/templates/system_spec.rb +9 -0
- data/lib/generators/rspec/view/templates/view_spec.rb +2 -2
- data/lib/generators/rspec/view/view_generator.rb +1 -0
- data/lib/generators/rspec.rb +20 -6
- data/lib/rspec/rails/active_record.rb +25 -0
- data/lib/rspec/rails/adapters.rb +104 -37
- data/lib/rspec/rails/configuration.rb +148 -0
- data/lib/rspec/rails/example/controller_example_group.rb +188 -138
- data/lib/rspec/rails/example/feature_example_group.rb +63 -20
- data/lib/rspec/rails/example/helper_example_group.rb +35 -26
- data/lib/rspec/rails/example/job_example_group.rb +23 -0
- data/lib/rspec/rails/example/mailer_example_group.rb +30 -14
- data/lib/rspec/rails/example/model_example_group.rb +8 -7
- data/lib/rspec/rails/example/rails_example_group.rb +3 -1
- data/lib/rspec/rails/example/request_example_group.rb +23 -16
- data/lib/rspec/rails/example/routing_example_group.rb +49 -40
- data/lib/rspec/rails/example/system_example_group.rb +108 -0
- data/lib/rspec/rails/example/view_example_group.rb +168 -135
- data/lib/rspec/rails/example.rb +2 -33
- data/lib/rspec/rails/extensions/active_record/proxy.rb +0 -1
- data/lib/rspec/rails/extensions.rb +0 -1
- data/lib/rspec/rails/feature_check.rb +64 -0
- data/lib/rspec/rails/file_fixture_support.rb +17 -0
- data/lib/rspec/rails/fixture_file_upload_support.rb +40 -0
- data/lib/rspec/rails/fixture_support.rb +32 -13
- data/lib/rspec/rails/matchers/active_job.rb +317 -0
- data/lib/rspec/rails/matchers/base_matcher.rb +184 -0
- data/lib/rspec/rails/matchers/be_a_new.rb +69 -62
- data/lib/rspec/rails/matchers/be_new_record.rb +24 -21
- data/lib/rspec/rails/matchers/be_valid.rb +42 -33
- data/lib/rspec/rails/matchers/have_enqueued_mail.rb +174 -0
- data/lib/rspec/rails/matchers/have_http_status.rb +381 -0
- data/lib/rspec/rails/matchers/have_rendered.rb +54 -31
- data/lib/rspec/rails/matchers/redirect_to.rb +30 -29
- data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
- data/lib/rspec/rails/matchers/routing_matchers.rb +107 -93
- data/lib/rspec/rails/matchers.rb +13 -14
- data/lib/rspec/rails/tasks/rspec.rake +1 -1
- data/lib/rspec/rails/vendor/capybara.rb +10 -4
- data/lib/rspec/rails/version.rb +3 -1
- data/lib/rspec/rails/view_assigns.rb +18 -18
- data/lib/rspec/rails/view_path_builder.rb +29 -0
- data/lib/rspec/rails/view_rendering.rb +89 -63
- data/lib/rspec/rails/view_spec_methods.rb +56 -0
- data/lib/rspec/rails.rb +10 -10
- data/lib/rspec-rails.rb +66 -1
- data.tar.gz.sig +0 -0
- metadata +92 -77
- metadata.gz.sig +0 -0
- data/lib/autotest/rails_rspec2.rb +0 -85
- data/lib/generators/rspec/install/templates/.rspec +0 -1
- data/lib/generators/rspec/install/templates/spec/spec_helper.rb.tt +0 -49
- data/lib/rspec/rails/extensions/active_record/base.rb +0 -58
- data/lib/rspec/rails/matchers/have_extension.rb +0 -36
- data/lib/rspec/rails/mocks.rb +0 -274
- data/lib/rspec/rails/module_inclusion.rb +0 -19
- data/lib/rspec/rails/vendor/webrat.rb +0 -33
@@ -0,0 +1,317 @@
|
|
1
|
+
require "active_job/base"
|
2
|
+
require "active_job/arguments"
|
3
|
+
|
4
|
+
module RSpec
|
5
|
+
module Rails
|
6
|
+
module Matchers
|
7
|
+
# Namespace for various implementations of ActiveJob features
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module ActiveJob
|
11
|
+
# rubocop: disable Style/ClassLength
|
12
|
+
# @private
|
13
|
+
class Base < RSpec::Rails::Matchers::BaseMatcher
|
14
|
+
def initialize
|
15
|
+
@args = []
|
16
|
+
@queue = nil
|
17
|
+
@at = nil
|
18
|
+
@block = Proc.new {}
|
19
|
+
set_expected_number(:exactly, 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def with(*args, &block)
|
23
|
+
@args = args
|
24
|
+
@block = block if block.present?
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_queue(queue)
|
29
|
+
@queue = queue
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def at(date)
|
34
|
+
@at = date
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def exactly(count)
|
39
|
+
set_expected_number(:exactly, count)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def at_least(count)
|
44
|
+
set_expected_number(:at_least, count)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def at_most(count)
|
49
|
+
set_expected_number(:at_most, count)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def times
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def once
|
58
|
+
exactly(:once)
|
59
|
+
end
|
60
|
+
|
61
|
+
def twice
|
62
|
+
exactly(:twice)
|
63
|
+
end
|
64
|
+
|
65
|
+
def thrice
|
66
|
+
exactly(:thrice)
|
67
|
+
end
|
68
|
+
|
69
|
+
def failure_message
|
70
|
+
"expected to enqueue #{base_message}".tap do |msg|
|
71
|
+
if @unmatching_jobs.any?
|
72
|
+
msg << "\nQueued jobs:"
|
73
|
+
@unmatching_jobs.each do |job|
|
74
|
+
msg << "\n #{base_job_message(job)}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def failure_message_when_negated
|
81
|
+
"expected not to enqueue #{base_message}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def message_expectation_modifier
|
85
|
+
case @expectation_type
|
86
|
+
when :exactly then "exactly"
|
87
|
+
when :at_most then "at most"
|
88
|
+
when :at_least then "at least"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def supports_block_expectations?
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def check(jobs)
|
99
|
+
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
|
100
|
+
if arguments_match?(job) && other_attributes_match?(job)
|
101
|
+
args = deserialize_arguments(job)
|
102
|
+
@block.call(*args)
|
103
|
+
true
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
@matching_jobs_count = @matching_jobs.size
|
109
|
+
|
110
|
+
case @expectation_type
|
111
|
+
when :exactly then @expected_number == @matching_jobs_count
|
112
|
+
when :at_most then @expected_number >= @matching_jobs_count
|
113
|
+
when :at_least then @expected_number <= @matching_jobs_count
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def base_message
|
118
|
+
"#{message_expectation_modifier} #{@expected_number} jobs,".tap do |msg|
|
119
|
+
msg << " with #{@args}," if @args.any?
|
120
|
+
msg << " on queue #{@queue}," if @queue
|
121
|
+
msg << " at #{@at.inspect}," if @at
|
122
|
+
msg << " but enqueued #{@matching_jobs_count}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def base_job_message(job)
|
127
|
+
msg_parts = []
|
128
|
+
msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
|
129
|
+
msg_parts << "on queue #{job[:queue]}" if job[:queue]
|
130
|
+
msg_parts << "at #{Time.at(job[:at])}" if job[:at]
|
131
|
+
|
132
|
+
"#{job[:job].name} job".tap do |msg|
|
133
|
+
msg << " #{msg_parts.join(', ')}" if msg_parts.any?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def arguments_match?(job)
|
138
|
+
if @args.any?
|
139
|
+
deserialized_args = deserialize_arguments(job)
|
140
|
+
RSpec::Mocks::ArgumentListMatcher.new(*@args).args_match?(*deserialized_args)
|
141
|
+
else
|
142
|
+
true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def other_attributes_match?(job)
|
147
|
+
serialized_attributes.all? { |key, value| value == job[key] }
|
148
|
+
end
|
149
|
+
|
150
|
+
def serialized_attributes
|
151
|
+
{}.tap do |attributes|
|
152
|
+
attributes[:at] = serialized_at if @at
|
153
|
+
attributes[:queue] = @queue if @queue
|
154
|
+
attributes[:job] = @job if @job
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def serialized_at
|
159
|
+
@at == :no_wait ? nil : @at.to_f
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_expected_number(relativity, count)
|
163
|
+
@expectation_type = relativity
|
164
|
+
@expected_number = case count
|
165
|
+
when :once then 1
|
166
|
+
when :twice then 2
|
167
|
+
when :thrice then 3
|
168
|
+
else Integer(count)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def deserialize_arguments(job)
|
173
|
+
::ActiveJob::Arguments.deserialize(job[:args])
|
174
|
+
rescue ::ActiveJob::DeserializationError
|
175
|
+
job[:args]
|
176
|
+
end
|
177
|
+
|
178
|
+
def queue_adapter
|
179
|
+
::ActiveJob::Base.queue_adapter
|
180
|
+
end
|
181
|
+
end
|
182
|
+
# rubocop: enable Style/ClassLength
|
183
|
+
|
184
|
+
# @private
|
185
|
+
class HaveEnqueuedJob < Base
|
186
|
+
def initialize(job)
|
187
|
+
super()
|
188
|
+
@job = job
|
189
|
+
end
|
190
|
+
|
191
|
+
def matches?(proc)
|
192
|
+
raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc
|
193
|
+
|
194
|
+
original_enqueued_jobs_count = queue_adapter.enqueued_jobs.count
|
195
|
+
proc.call
|
196
|
+
in_block_jobs = queue_adapter.enqueued_jobs.drop(original_enqueued_jobs_count)
|
197
|
+
|
198
|
+
check(in_block_jobs)
|
199
|
+
end
|
200
|
+
|
201
|
+
def does_not_match?(proc)
|
202
|
+
set_expected_number(:at_least, 1)
|
203
|
+
|
204
|
+
!matches?(proc)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# @private
|
209
|
+
class HaveBeenEnqueued < Base
|
210
|
+
def matches?(job)
|
211
|
+
@job = job
|
212
|
+
check(queue_adapter.enqueued_jobs)
|
213
|
+
end
|
214
|
+
|
215
|
+
def does_not_match?(proc)
|
216
|
+
set_expected_number(:at_least, 1)
|
217
|
+
|
218
|
+
!matches?(proc)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# @api public
|
224
|
+
# Passes if a job has been enqueued inside block. May chain at_least, at_most or exactly to specify a number of times.
|
225
|
+
#
|
226
|
+
# @example
|
227
|
+
# expect {
|
228
|
+
# HeavyLiftingJob.perform_later
|
229
|
+
# }.to have_enqueued_job
|
230
|
+
#
|
231
|
+
# # Using alias
|
232
|
+
# expect {
|
233
|
+
# HeavyLiftingJob.perform_later
|
234
|
+
# }.to enqueue_job
|
235
|
+
#
|
236
|
+
# expect {
|
237
|
+
# HelloJob.perform_later
|
238
|
+
# HeavyLiftingJob.perform_later
|
239
|
+
# }.to have_enqueued_job(HelloJob).exactly(:once)
|
240
|
+
#
|
241
|
+
# expect {
|
242
|
+
# 3.times { HelloJob.perform_later }
|
243
|
+
# }.to have_enqueued_job(HelloJob).at_least(2).times
|
244
|
+
#
|
245
|
+
# expect {
|
246
|
+
# HelloJob.perform_later
|
247
|
+
# }.to have_enqueued_job(HelloJob).at_most(:twice)
|
248
|
+
#
|
249
|
+
# expect {
|
250
|
+
# HelloJob.perform_later
|
251
|
+
# HeavyLiftingJob.perform_later
|
252
|
+
# }.to have_enqueued_job(HelloJob).and have_enqueued_job(HeavyLiftingJob)
|
253
|
+
#
|
254
|
+
# expect {
|
255
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
256
|
+
# }.to have_enqueued_job.with(42).on_queue("low").at(Date.tomorrow.noon)
|
257
|
+
#
|
258
|
+
# expect {
|
259
|
+
# HelloJob.set(queue: "low").perform_later(42)
|
260
|
+
# }.to have_enqueued_job.with(42).on_queue("low").at(:no_wait)
|
261
|
+
#
|
262
|
+
# expect {
|
263
|
+
# HelloJob.perform_later('rspec_rails', 'rails', 42)
|
264
|
+
# }.to have_enqueued_job.with { |from, to, times|
|
265
|
+
# # Perform more complex argument matching using dynamic arguments
|
266
|
+
# expect(from).to include "_#{to}"
|
267
|
+
# }
|
268
|
+
def have_enqueued_job(job = nil)
|
269
|
+
check_active_job_adapter
|
270
|
+
ActiveJob::HaveEnqueuedJob.new(job)
|
271
|
+
end
|
272
|
+
alias_method :enqueue_job, :have_enqueued_job
|
273
|
+
|
274
|
+
# @api public
|
275
|
+
# Passes if a job has been enqueued. May chain at_least, at_most or exactly to specify a number of times.
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
# before { ActiveJob::Base.queue_adapter.enqueued_jobs.clear }
|
279
|
+
#
|
280
|
+
# HeavyLiftingJob.perform_later
|
281
|
+
# expect(HeavyLiftingJob).to have_been_enqueued
|
282
|
+
#
|
283
|
+
# HelloJob.perform_later
|
284
|
+
# HeavyLiftingJob.perform_later
|
285
|
+
# expect(HeavyLiftingJob).to have_been_enqueued.exactly(:once)
|
286
|
+
#
|
287
|
+
# 3.times { HelloJob.perform_later }
|
288
|
+
# expect(HelloJob).to have_been_enqueued.at_least(2).times
|
289
|
+
#
|
290
|
+
# HelloJob.perform_later
|
291
|
+
# expect(HelloJob).to enqueue_job(HelloJob).at_most(:twice)
|
292
|
+
#
|
293
|
+
# HelloJob.perform_later
|
294
|
+
# HeavyLiftingJob.perform_later
|
295
|
+
# expect(HelloJob).to have_been_enqueued
|
296
|
+
# expect(HeavyLiftingJob).to have_been_enqueued
|
297
|
+
#
|
298
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
299
|
+
# expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(Date.tomorrow.noon)
|
300
|
+
#
|
301
|
+
# HelloJob.set(queue: "low").perform_later(42)
|
302
|
+
# expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(:no_wait)
|
303
|
+
def have_been_enqueued
|
304
|
+
check_active_job_adapter
|
305
|
+
ActiveJob::HaveBeenEnqueued.new
|
306
|
+
end
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
# @private
|
311
|
+
def check_active_job_adapter
|
312
|
+
return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
|
313
|
+
raise StandardError, "To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
# @ api private
|
5
|
+
class BaseMatcher
|
6
|
+
include RSpec::Matchers::Composable
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
# Used to detect when no arg is passed to `initialize`.
|
10
|
+
# `nil` cannot be used because it's a valid value to pass.
|
11
|
+
UNDEFINED = Object.new.freeze
|
12
|
+
|
13
|
+
# @private
|
14
|
+
attr_reader :actual, :expected, :rescued_exception
|
15
|
+
|
16
|
+
# @private
|
17
|
+
attr_writer :matcher_name
|
18
|
+
|
19
|
+
def initialize(expected = UNDEFINED)
|
20
|
+
@expected = expected unless UNDEFINED.equal?(expected)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
# Indicates if the match is successful. Delegates to `match`, which
|
25
|
+
# should be defined on a subclass. Takes care of consistently
|
26
|
+
# initializing the `actual` attribute.
|
27
|
+
def matches?(actual)
|
28
|
+
@actual = actual
|
29
|
+
match(expected, actual)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
# Used to wrap a block of code that will indicate failure by
|
34
|
+
# raising one of the named exceptions.
|
35
|
+
#
|
36
|
+
# This is used by rspec-rails for some of its matchers that
|
37
|
+
# wrap rails' assertions.
|
38
|
+
def match_unless_raises(*exceptions)
|
39
|
+
exceptions.unshift Exception if exceptions.empty?
|
40
|
+
begin
|
41
|
+
yield
|
42
|
+
true
|
43
|
+
rescue *exceptions => @rescued_exception
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
# Generates a description using {RSpec::Matchers::EnglishPhrasing}.
|
50
|
+
# @return [String]
|
51
|
+
def description
|
52
|
+
desc = RSpec::Matchers::EnglishPhrasing.split_words(self.class.matcher_name)
|
53
|
+
desc << RSpec::Matchers::EnglishPhrasing.list(@expected) if defined?(@expected)
|
54
|
+
desc
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
# Matchers are not diffable by default. Override this to make your
|
59
|
+
# subclass diffable.
|
60
|
+
def diffable?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
# Most matchers are value matchers (i.e. meant to work with `expect(value)`)
|
66
|
+
# rather than block matchers (i.e. meant to work with `expect { }`), so
|
67
|
+
# this defaults to false. Block matchers must override this to return true.
|
68
|
+
def supports_block_expectations?
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
# @api private
|
73
|
+
def expects_call_stack_jump?
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
# @private
|
78
|
+
def expected_formatted
|
79
|
+
RSpec::Support::ObjectFormatter.format(@expected)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @private
|
83
|
+
def actual_formatted
|
84
|
+
RSpec::Support::ObjectFormatter.format(@actual)
|
85
|
+
end
|
86
|
+
|
87
|
+
# @private
|
88
|
+
def self.matcher_name
|
89
|
+
@matcher_name ||= underscore(name.split('::').last)
|
90
|
+
end
|
91
|
+
|
92
|
+
# @private
|
93
|
+
def matcher_name
|
94
|
+
if defined?(@matcher_name)
|
95
|
+
@matcher_name
|
96
|
+
else
|
97
|
+
self.class.matcher_name
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
# Borrowed from ActiveSupport.
|
103
|
+
def self.underscore(camel_cased_word)
|
104
|
+
word = camel_cased_word.to_s.dup
|
105
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
106
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
107
|
+
word.tr!('-', '_')
|
108
|
+
word.downcase!
|
109
|
+
word
|
110
|
+
end
|
111
|
+
private_class_method :underscore
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def assert_ivars(*expected_ivars)
|
116
|
+
return unless (expected_ivars - present_ivars).any?
|
117
|
+
ivar_list = RSpec::Matchers::EnglishPhrasing.list(expected_ivars)
|
118
|
+
raise "#{self.class.name} needs to supply#{ivar_list}"
|
119
|
+
end
|
120
|
+
|
121
|
+
if RUBY_VERSION.to_f < 1.9
|
122
|
+
# :nocov:
|
123
|
+
def present_ivars
|
124
|
+
instance_variables.map(&:to_sym)
|
125
|
+
end
|
126
|
+
# :nocov:
|
127
|
+
else
|
128
|
+
alias present_ivars instance_variables
|
129
|
+
end
|
130
|
+
|
131
|
+
# @private
|
132
|
+
module HashFormatting
|
133
|
+
# `{ :a => 5, :b => 2 }.inspect` produces:
|
134
|
+
#
|
135
|
+
# {:a=>5, :b=>2}
|
136
|
+
#
|
137
|
+
# ...but it looks much better as:
|
138
|
+
#
|
139
|
+
# {:a => 5, :b => 2}
|
140
|
+
#
|
141
|
+
# This is idempotent and safe to run on a string multiple times.
|
142
|
+
def improve_hash_formatting(inspect_string)
|
143
|
+
inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
|
144
|
+
end
|
145
|
+
module_function :improve_hash_formatting
|
146
|
+
end
|
147
|
+
|
148
|
+
include HashFormatting
|
149
|
+
|
150
|
+
# @api private
|
151
|
+
# Provides default implementations of failure messages, based on the `description`.
|
152
|
+
module DefaultFailureMessages
|
153
|
+
# @api private
|
154
|
+
# Provides a good generic failure message. Based on `description`.
|
155
|
+
# When subclassing, if you are not satisfied with this failure message
|
156
|
+
# you often only need to override `description`.
|
157
|
+
# @return [String]
|
158
|
+
def failure_message
|
159
|
+
"expected #{description_of @actual} to #{description}".dup
|
160
|
+
end
|
161
|
+
|
162
|
+
# @api private
|
163
|
+
# Provides a good generic negative failure message. Based on `description`.
|
164
|
+
# When subclassing, if you are not satisfied with this failure message
|
165
|
+
# you often only need to override `description`.
|
166
|
+
# @return [String]
|
167
|
+
def failure_message_when_negated
|
168
|
+
"expected #{description_of @actual} not to #{description}".dup
|
169
|
+
end
|
170
|
+
|
171
|
+
# @private
|
172
|
+
def self.has_default_failure_messages?(matcher)
|
173
|
+
matcher.method(:failure_message).owner == self &&
|
174
|
+
matcher.method(:failure_message_when_negated).owner == self
|
175
|
+
rescue NameError
|
176
|
+
false
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
include DefaultFailureMessages
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -1,76 +1,83 @@
|
|
1
|
-
module RSpec
|
2
|
-
|
1
|
+
module RSpec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
# Matcher class for `be_a_new`. Should not be instantiated directly.
|
7
|
+
#
|
8
|
+
# @see RSpec::Rails::Matchers#be_a_new
|
9
|
+
class BeANew < RSpec::Rails::Matchers::BaseMatcher
|
10
|
+
# @private
|
11
|
+
def initialize(expected)
|
12
|
+
@expected = expected
|
13
|
+
end
|
3
14
|
|
4
|
-
|
5
|
-
|
6
|
-
|
15
|
+
# @private
|
16
|
+
def matches?(actual)
|
17
|
+
@actual = actual
|
18
|
+
actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
|
19
|
+
end
|
7
20
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
21
|
+
# @api public
|
22
|
+
# @see RSpec::Rails::Matchers#be_a_new
|
23
|
+
def with(expected_attributes)
|
24
|
+
attributes.merge!(expected_attributes)
|
25
|
+
self
|
26
|
+
end
|
13
27
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
# @private
|
29
|
+
def failure_message
|
30
|
+
[].tap do |message|
|
31
|
+
unless actual.is_a?(expected) && actual.new_record?
|
32
|
+
message << "expected #{actual.inspect} to be a new #{expected.inspect}"
|
33
|
+
end
|
34
|
+
unless attributes_match?(actual)
|
35
|
+
describe_unmatched_attributes = surface_descriptions_in(unmatched_attributes)
|
36
|
+
if unmatched_attributes.size > 1
|
37
|
+
message << "attributes #{describe_unmatched_attributes.inspect} were not set on #{actual.inspect}"
|
38
|
+
else
|
39
|
+
message << "attribute #{describe_unmatched_attributes.inspect} was not set on #{actual.inspect}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end.join(' and ')
|
43
|
+
end
|
26
44
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
message << "expected #{actual.inspect} to be a new #{expected.inspect}"
|
45
|
+
private
|
46
|
+
|
47
|
+
def attributes
|
48
|
+
@attributes ||= {}
|
32
49
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
message << "attribute #{unmatched_attributes.inspect} was not set on #{actual.inspect}"
|
50
|
+
|
51
|
+
def attributes_match?(actual)
|
52
|
+
attributes.stringify_keys.all? do |key, value|
|
53
|
+
values_match?(value, actual.attributes[key])
|
38
54
|
end
|
39
55
|
end
|
40
|
-
end.join(' and ')
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
56
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
attributes.stringify_keys.all? do |key, value|
|
51
|
-
actual.attributes[key].eql?(value)
|
57
|
+
def unmatched_attributes
|
58
|
+
attributes.stringify_keys.reject do |key, value|
|
59
|
+
values_match?(value, actual.attributes[key])
|
60
|
+
end
|
61
|
+
end
|
52
62
|
end
|
53
|
-
end
|
54
63
|
|
55
|
-
|
56
|
-
|
57
|
-
|
64
|
+
# @api public
|
65
|
+
# Passes if actual is an instance of `model_class` and returns `true` for
|
66
|
+
# `new_record?`. Typically used to specify instance variables assigned to
|
67
|
+
# views by controller actions
|
68
|
+
#
|
69
|
+
# Use the `with` method to specify the specific attributes to match on the
|
70
|
+
# new record.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# get :new
|
74
|
+
# assigns(:thing).should be_a_new(Thing)
|
75
|
+
#
|
76
|
+
# post :create, :thing => { :name => "Illegal Value" }
|
77
|
+
# assigns(:thing).should be_a_new(Thing).with(:name => nil)
|
78
|
+
def be_a_new(model_class)
|
79
|
+
BeANew.new(model_class)
|
58
80
|
end
|
59
81
|
end
|
60
82
|
end
|
61
|
-
|
62
|
-
# Passes if actual is an instance of `model_class` and returns `false` for
|
63
|
-
# `persisted?`. Typically used to specify instance variables assigned to
|
64
|
-
# views by controller actions
|
65
|
-
#
|
66
|
-
# @example
|
67
|
-
#
|
68
|
-
# get :new
|
69
|
-
# assigns(:thing).should be_a_new(Thing)
|
70
|
-
#
|
71
|
-
# post :create, :thing => { :name => "Illegal Value" }
|
72
|
-
# assigns(:thing).should be_a_new(Thing).with(:name => nil)
|
73
|
-
def be_a_new(model_class)
|
74
|
-
BeANew.new(model_class)
|
75
|
-
end
|
76
83
|
end
|
@@ -1,27 +1,30 @@
|
|
1
|
-
module RSpec
|
2
|
-
|
1
|
+
module RSpec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
# @private
|
5
|
+
class BeANewRecord < RSpec::Rails::Matchers::BaseMatcher
|
6
|
+
def matches?(actual)
|
7
|
+
actual.new_record?
|
8
|
+
end
|
3
9
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
10
|
+
def failure_message
|
11
|
+
"expected #{actual.inspect} to be a new record, but was persisted"
|
12
|
+
end
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
def failure_message_when_negated
|
15
|
+
"expected #{actual.inspect} to be persisted, but was a new record"
|
16
|
+
end
|
17
|
+
end
|
12
18
|
|
13
|
-
|
14
|
-
|
19
|
+
# @api public
|
20
|
+
# Passes if actual returns `true` for `new_record?`.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# get :new
|
24
|
+
# expect(assigns(:thing)).to be_new_record
|
25
|
+
def be_new_record
|
26
|
+
BeANewRecord.new
|
27
|
+
end
|
15
28
|
end
|
16
29
|
end
|
17
|
-
|
18
|
-
# Passes if actual returns `false` for `persisted?`.
|
19
|
-
#
|
20
|
-
# @example
|
21
|
-
#
|
22
|
-
# get :new
|
23
|
-
# assigns(:thing).should be_new_record
|
24
|
-
def be_new_record
|
25
|
-
BeANewRecord.new
|
26
|
-
end
|
27
30
|
end
|