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.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +4 -2
  5. data/Capybara.md +2 -4
  6. data/Changelog.md +592 -34
  7. data/{License.txt → LICENSE.md} +5 -2
  8. data/README.md +290 -369
  9. data/lib/generators/rspec/controller/controller_generator.rb +1 -0
  10. data/lib/generators/rspec/controller/templates/controller_spec.rb +5 -5
  11. data/lib/generators/rspec/controller/templates/view_spec.rb +2 -2
  12. data/lib/generators/rspec/feature/feature_generator.rb +29 -0
  13. data/lib/generators/rspec/feature/templates/feature_singular_spec.rb +5 -0
  14. data/lib/generators/rspec/feature/templates/feature_spec.rb +5 -0
  15. data/lib/generators/rspec/generators/generator_generator.rb +24 -0
  16. data/lib/generators/rspec/generators/templates/generator_spec.rb +6 -0
  17. data/lib/generators/rspec/helper/helper_generator.rb +1 -0
  18. data/lib/generators/rspec/helper/templates/helper_spec.rb +2 -2
  19. data/lib/generators/rspec/install/install_generator.rb +44 -5
  20. data/lib/generators/rspec/install/templates/spec/rails_helper.rb +78 -0
  21. data/lib/generators/rspec/integration/integration_generator.rb +8 -13
  22. data/lib/generators/rspec/integration/templates/request_spec.rb +4 -9
  23. data/lib/generators/rspec/job/job_generator.rb +12 -0
  24. data/lib/generators/rspec/job/templates/job_spec.rb.erb +7 -0
  25. data/lib/generators/rspec/mailer/mailer_generator.rb +7 -0
  26. data/lib/generators/rspec/mailer/templates/mailer_spec.rb +7 -7
  27. data/lib/generators/rspec/mailer/templates/preview.rb +13 -0
  28. data/lib/generators/rspec/model/model_generator.rb +19 -5
  29. data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
  30. data/lib/generators/rspec/model/templates/model_spec.rb +2 -2
  31. data/lib/generators/rspec/observer/observer_generator.rb +1 -0
  32. data/lib/generators/rspec/observer/templates/observer_spec.rb +2 -2
  33. data/lib/generators/rspec/request/request_generator.rb +10 -0
  34. data/lib/generators/rspec/scaffold/scaffold_generator.rb +68 -138
  35. data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +165 -0
  36. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +98 -73
  37. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +9 -13
  38. data/lib/generators/rspec/scaffold/templates/index_spec.rb +3 -10
  39. data/lib/generators/rspec/scaffold/templates/new_spec.rb +10 -14
  40. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +21 -12
  41. data/lib/generators/rspec/scaffold/templates/show_spec.rb +4 -11
  42. data/lib/generators/rspec/system/system_generator.rb +26 -0
  43. data/lib/generators/rspec/system/templates/system_spec.rb +9 -0
  44. data/lib/generators/rspec/view/templates/view_spec.rb +2 -2
  45. data/lib/generators/rspec/view/view_generator.rb +1 -0
  46. data/lib/generators/rspec.rb +20 -6
  47. data/lib/rspec/rails/active_record.rb +25 -0
  48. data/lib/rspec/rails/adapters.rb +104 -37
  49. data/lib/rspec/rails/configuration.rb +148 -0
  50. data/lib/rspec/rails/example/controller_example_group.rb +188 -138
  51. data/lib/rspec/rails/example/feature_example_group.rb +63 -20
  52. data/lib/rspec/rails/example/helper_example_group.rb +35 -26
  53. data/lib/rspec/rails/example/job_example_group.rb +23 -0
  54. data/lib/rspec/rails/example/mailer_example_group.rb +30 -14
  55. data/lib/rspec/rails/example/model_example_group.rb +8 -7
  56. data/lib/rspec/rails/example/rails_example_group.rb +3 -1
  57. data/lib/rspec/rails/example/request_example_group.rb +23 -16
  58. data/lib/rspec/rails/example/routing_example_group.rb +49 -40
  59. data/lib/rspec/rails/example/system_example_group.rb +108 -0
  60. data/lib/rspec/rails/example/view_example_group.rb +168 -135
  61. data/lib/rspec/rails/example.rb +2 -33
  62. data/lib/rspec/rails/extensions/active_record/proxy.rb +0 -1
  63. data/lib/rspec/rails/extensions.rb +0 -1
  64. data/lib/rspec/rails/feature_check.rb +64 -0
  65. data/lib/rspec/rails/file_fixture_support.rb +17 -0
  66. data/lib/rspec/rails/fixture_file_upload_support.rb +40 -0
  67. data/lib/rspec/rails/fixture_support.rb +32 -13
  68. data/lib/rspec/rails/matchers/active_job.rb +317 -0
  69. data/lib/rspec/rails/matchers/base_matcher.rb +184 -0
  70. data/lib/rspec/rails/matchers/be_a_new.rb +69 -62
  71. data/lib/rspec/rails/matchers/be_new_record.rb +24 -21
  72. data/lib/rspec/rails/matchers/be_valid.rb +42 -33
  73. data/lib/rspec/rails/matchers/have_enqueued_mail.rb +174 -0
  74. data/lib/rspec/rails/matchers/have_http_status.rb +381 -0
  75. data/lib/rspec/rails/matchers/have_rendered.rb +54 -31
  76. data/lib/rspec/rails/matchers/redirect_to.rb +30 -29
  77. data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
  78. data/lib/rspec/rails/matchers/routing_matchers.rb +107 -93
  79. data/lib/rspec/rails/matchers.rb +13 -14
  80. data/lib/rspec/rails/tasks/rspec.rake +1 -1
  81. data/lib/rspec/rails/vendor/capybara.rb +10 -4
  82. data/lib/rspec/rails/version.rb +3 -1
  83. data/lib/rspec/rails/view_assigns.rb +18 -18
  84. data/lib/rspec/rails/view_path_builder.rb +29 -0
  85. data/lib/rspec/rails/view_rendering.rb +89 -63
  86. data/lib/rspec/rails/view_spec_methods.rb +56 -0
  87. data/lib/rspec/rails.rb +10 -10
  88. data/lib/rspec-rails.rb +66 -1
  89. data.tar.gz.sig +0 -0
  90. metadata +92 -77
  91. metadata.gz.sig +0 -0
  92. data/lib/autotest/rails_rspec2.rb +0 -85
  93. data/lib/generators/rspec/install/templates/.rspec +0 -1
  94. data/lib/generators/rspec/install/templates/spec/spec_helper.rb.tt +0 -49
  95. data/lib/rspec/rails/extensions/active_record/base.rb +0 -58
  96. data/lib/rspec/rails/matchers/have_extension.rb +0 -36
  97. data/lib/rspec/rails/mocks.rb +0 -274
  98. data/lib/rspec/rails/module_inclusion.rb +0 -19
  99. 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::Rails::Matchers
2
- class BeANew < RSpec::Matchers::BuiltIn::BaseMatcher
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
- def initialize(expected)
5
- @expected = expected
6
- end
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
- # @api private
9
- def matches?(actual)
10
- @actual = actual
11
- actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
12
- end
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
- # Use this to specify the specific attributes to match on the new record.
15
- #
16
- # @example
17
- #
18
- # it "assigns a new Thing with the submitted attributes" do
19
- # post :create, :thing => { :name => "Illegal Value" }
20
- # assigns(:thing).should be_a_new(Thing).with(:name => nil)
21
- # end
22
- def with(expected_attributes)
23
- attributes.merge!(expected_attributes)
24
- self
25
- end
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
- # @api private
28
- def failure_message_for_should
29
- [].tap do |message|
30
- unless actual.is_a?(expected) && actual.new_record?
31
- message << "expected #{actual.inspect} to be a new #{expected.inspect}"
45
+ private
46
+
47
+ def attributes
48
+ @attributes ||= {}
32
49
  end
33
- unless attributes_match?(actual)
34
- if unmatched_attributes.size > 1
35
- message << "attributes #{unmatched_attributes.inspect} were not set on #{actual.inspect}"
36
- else
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
- def attributes
46
- @attributes ||= {}
47
- end
48
-
49
- def attributes_match?(actual)
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
- def unmatched_attributes
56
- attributes.stringify_keys.reject do |key, value|
57
- actual.attributes[key].eql?(value)
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::Rails::Matchers
2
- class BeANewRecord < RSpec::Matchers::BuiltIn::BaseMatcher
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
- # @api private
5
- def matches?(actual)
6
- !actual.persisted?
7
- end
10
+ def failure_message
11
+ "expected #{actual.inspect} to be a new record, but was persisted"
12
+ end
8
13
 
9
- def failure_message_for_should
10
- "expected #{actual.inspect} to be a new record, but was persisted"
11
- end
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
- def failure_message_for_should_not
14
- "expected #{actual.inspect} to be persisted, but was a new record"
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