rspec-rails 1.3.2 → 7.0.2

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 (268) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +4 -6
  4. data/.yardopts +11 -0
  5. data/Capybara.md +28 -0
  6. data/Changelog.md +1343 -0
  7. data/LICENSE.md +25 -0
  8. data/README.md +383 -0
  9. data/lib/generators/rspec/channel/channel_generator.rb +12 -0
  10. data/lib/generators/rspec/channel/templates/channel_spec.rb.erb +7 -0
  11. data/lib/generators/rspec/controller/controller_generator.rb +51 -0
  12. data/lib/generators/rspec/controller/templates/controller_spec.rb +16 -0
  13. data/lib/generators/rspec/controller/templates/request_spec.rb +19 -0
  14. data/lib/generators/rspec/controller/templates/routing_spec.rb +13 -0
  15. data/lib/generators/rspec/controller/templates/view_spec.rb +5 -0
  16. data/lib/generators/rspec/feature/feature_generator.rb +29 -0
  17. data/lib/generators/rspec/feature/templates/feature_singular_spec.rb +5 -0
  18. data/lib/generators/rspec/feature/templates/feature_spec.rb +5 -0
  19. data/lib/generators/rspec/generator/generator_generator.rb +24 -0
  20. data/lib/generators/rspec/generator/templates/generator_spec.rb +5 -0
  21. data/lib/generators/rspec/helper/helper_generator.rb +16 -0
  22. data/lib/generators/rspec/helper/templates/helper_spec.rb +17 -0
  23. data/lib/generators/rspec/install/install_generator.rb +80 -0
  24. data/lib/generators/rspec/install/templates/spec/rails_helper.rb +89 -0
  25. data/lib/generators/rspec/job/job_generator.rb +13 -0
  26. data/lib/generators/rspec/job/templates/job_spec.rb.erb +7 -0
  27. data/lib/generators/rspec/mailbox/mailbox_generator.rb +14 -0
  28. data/lib/generators/rspec/mailbox/templates/mailbox_spec.rb.erb +7 -0
  29. data/lib/generators/rspec/mailer/mailer_generator.rb +30 -0
  30. data/lib/generators/rspec/mailer/templates/fixture +3 -0
  31. data/lib/generators/rspec/mailer/templates/mailer_spec.rb +25 -0
  32. data/lib/generators/rspec/mailer/templates/preview.rb +13 -0
  33. data/lib/generators/rspec/model/model_generator.rb +37 -0
  34. data/lib/generators/rspec/model/templates/fixtures.yml +19 -0
  35. data/lib/generators/rspec/model/templates/model_spec.rb +7 -0
  36. data/lib/generators/rspec/request/request_generator.rb +17 -0
  37. data/lib/generators/rspec/request/templates/request_spec.rb +10 -0
  38. data/lib/generators/rspec/scaffold/scaffold_generator.rb +136 -0
  39. data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +129 -0
  40. data/lib/generators/rspec/scaffold/templates/api_request_spec.rb +131 -0
  41. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +145 -0
  42. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +27 -0
  43. data/lib/generators/rspec/scaffold/templates/index_spec.rb +26 -0
  44. data/lib/generators/rspec/scaffold/templates/new_spec.rb +22 -0
  45. data/lib/generators/rspec/scaffold/templates/request_spec.rb +138 -0
  46. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +46 -0
  47. data/lib/generators/rspec/scaffold/templates/show_spec.rb +21 -0
  48. data/lib/generators/rspec/system/system_generator.rb +24 -0
  49. data/lib/generators/rspec/system/templates/system_spec.rb +9 -0
  50. data/lib/generators/rspec/view/templates/view_spec.rb +5 -0
  51. data/lib/generators/rspec/view/view_generator.rb +22 -0
  52. data/lib/generators/rspec.rb +56 -0
  53. data/lib/rspec/rails/active_record.rb +25 -0
  54. data/lib/rspec/rails/adapters.rb +196 -0
  55. data/lib/rspec/rails/configuration.rb +222 -0
  56. data/lib/rspec/rails/example/channel_example_group.rb +93 -0
  57. data/lib/rspec/rails/example/controller_example_group.rb +217 -0
  58. data/lib/rspec/rails/example/feature_example_group.rb +53 -0
  59. data/lib/rspec/rails/example/helper_example_group.rb +42 -0
  60. data/lib/rspec/rails/example/job_example_group.rb +23 -0
  61. data/lib/rspec/rails/example/mailbox_example_group.rb +80 -0
  62. data/lib/rspec/rails/example/mailer_example_group.rb +38 -0
  63. data/lib/rspec/rails/example/model_example_group.rb +11 -0
  64. data/lib/rspec/rails/example/rails_example_group.rb +23 -0
  65. data/lib/rspec/rails/example/request_example_group.rb +27 -0
  66. data/lib/rspec/rails/example/routing_example_group.rb +61 -0
  67. data/lib/rspec/rails/example/system_example_group.rb +180 -0
  68. data/lib/rspec/rails/example/view_example_group.rb +214 -0
  69. data/lib/rspec/rails/example.rb +13 -0
  70. data/lib/rspec/rails/extensions/active_record/proxy.rb +11 -0
  71. data/lib/rspec/rails/extensions.rb +1 -0
  72. data/lib/rspec/rails/feature_check.rb +51 -0
  73. data/lib/rspec/rails/file_fixture_support.rb +18 -0
  74. data/lib/rspec/rails/fixture_file_upload_support.rb +45 -0
  75. data/lib/rspec/rails/fixture_support.rb +88 -0
  76. data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +173 -0
  77. data/lib/rspec/rails/matchers/action_cable/have_streams.rb +58 -0
  78. data/lib/rspec/rails/matchers/action_cable.rb +65 -0
  79. data/lib/rspec/rails/matchers/action_mailbox.rb +73 -0
  80. data/lib/rspec/rails/matchers/active_job.rb +517 -0
  81. data/lib/rspec/rails/matchers/base_matcher.rb +179 -0
  82. data/lib/rspec/rails/matchers/be_a_new.rb +83 -0
  83. data/lib/rspec/rails/matchers/be_new_record.rb +30 -0
  84. data/lib/rspec/rails/matchers/be_valid.rb +49 -0
  85. data/lib/rspec/rails/matchers/have_enqueued_mail.rb +258 -0
  86. data/lib/rspec/rails/matchers/have_http_status.rb +381 -0
  87. data/lib/rspec/rails/matchers/have_rendered.rb +64 -0
  88. data/lib/rspec/rails/matchers/redirect_to.rb +38 -0
  89. data/lib/rspec/rails/matchers/relation_match_array.rb +3 -0
  90. data/lib/rspec/rails/matchers/routing_matchers.rb +125 -0
  91. data/lib/rspec/rails/matchers/send_email.rb +122 -0
  92. data/lib/rspec/rails/matchers.rb +36 -0
  93. data/lib/rspec/rails/tasks/rspec.rake +49 -0
  94. data/lib/rspec/rails/vendor/capybara.rb +32 -0
  95. data/lib/rspec/rails/version.rb +9 -0
  96. data/lib/rspec/rails/view_assigns.rb +27 -0
  97. data/lib/rspec/rails/view_path_builder.rb +29 -0
  98. data/lib/rspec/rails/view_rendering.rb +166 -0
  99. data/lib/rspec/rails/view_spec_methods.rb +56 -0
  100. data/lib/rspec/rails.rb +18 -0
  101. data/lib/rspec-rails.rb +97 -0
  102. data.tar.gz.sig +0 -0
  103. metadata +307 -261
  104. metadata.gz.sig +0 -0
  105. data/Contribute.rdoc +0 -4
  106. data/History.rdoc +0 -302
  107. data/License.txt +0 -33
  108. data/Manifest.txt +0 -165
  109. data/README.rdoc +0 -45
  110. data/Rakefile +0 -72
  111. data/TODO.txt +0 -17
  112. data/Upgrade.rdoc +0 -148
  113. data/generators/integration_spec/integration_spec_generator.rb +0 -10
  114. data/generators/integration_spec/templates/integration_spec.rb +0 -4
  115. data/generators/rspec/CHANGES +0 -1
  116. data/generators/rspec/rspec_generator.rb +0 -72
  117. data/generators/rspec/templates/previous_failures.txt +0 -0
  118. data/generators/rspec/templates/rcov.opts +0 -2
  119. data/generators/rspec/templates/rspec.rake +0 -144
  120. data/generators/rspec/templates/script/autospec +0 -6
  121. data/generators/rspec/templates/script/spec +0 -10
  122. data/generators/rspec/templates/spec.opts +0 -4
  123. data/generators/rspec/templates/spec_helper.rb +0 -54
  124. data/generators/rspec_controller/USAGE +0 -33
  125. data/generators/rspec_controller/rspec_controller_generator.rb +0 -47
  126. data/generators/rspec_controller/templates/controller_spec.rb +0 -25
  127. data/generators/rspec_controller/templates/helper_spec.rb +0 -11
  128. data/generators/rspec_controller/templates/view_spec.rb +0 -12
  129. data/generators/rspec_default_values.rb +0 -28
  130. data/generators/rspec_model/USAGE +0 -18
  131. data/generators/rspec_model/rspec_model_generator.rb +0 -35
  132. data/generators/rspec_model/templates/model_spec.rb +0 -13
  133. data/generators/rspec_scaffold/rspec_scaffold_generator.rb +0 -154
  134. data/generators/rspec_scaffold/templates/controller_spec.rb +0 -131
  135. data/generators/rspec_scaffold/templates/edit_erb_spec.rb +0 -25
  136. data/generators/rspec_scaffold/templates/helper_spec.rb +0 -11
  137. data/generators/rspec_scaffold/templates/index_erb_spec.rb +0 -27
  138. data/generators/rspec_scaffold/templates/new_erb_spec.rb +0 -25
  139. data/generators/rspec_scaffold/templates/routing_spec.rb +0 -33
  140. data/generators/rspec_scaffold/templates/show_erb_spec.rb +0 -22
  141. data/init.rb +0 -9
  142. data/lib/autotest/discover.rb +0 -5
  143. data/lib/autotest/rails_rspec.rb +0 -76
  144. data/lib/spec/rails/example/assigns_hash_proxy.rb +0 -39
  145. data/lib/spec/rails/example/controller_example_group.rb +0 -285
  146. data/lib/spec/rails/example/cookies_proxy.rb +0 -29
  147. data/lib/spec/rails/example/functional_example_group.rb +0 -106
  148. data/lib/spec/rails/example/helper_example_group.rb +0 -153
  149. data/lib/spec/rails/example/integration_example_group.rb +0 -16
  150. data/lib/spec/rails/example/model_example_group.rb +0 -15
  151. data/lib/spec/rails/example/render_observer.rb +0 -80
  152. data/lib/spec/rails/example/routing_example_group.rb +0 -13
  153. data/lib/spec/rails/example/routing_helpers.rb +0 -66
  154. data/lib/spec/rails/example/view_example_group.rb +0 -199
  155. data/lib/spec/rails/example.rb +0 -48
  156. data/lib/spec/rails/extensions/action_controller/rescue.rb +0 -42
  157. data/lib/spec/rails/extensions/action_controller/test_case.rb +0 -16
  158. data/lib/spec/rails/extensions/action_controller/test_response.rb +0 -21
  159. data/lib/spec/rails/extensions/action_view/base.rb +0 -33
  160. data/lib/spec/rails/extensions/active_record/base.rb +0 -45
  161. data/lib/spec/rails/extensions/active_support/test_case.rb +0 -7
  162. data/lib/spec/rails/extensions/spec/matchers/have.rb +0 -23
  163. data/lib/spec/rails/extensions/spec/runner/configuration.rb +0 -44
  164. data/lib/spec/rails/extensions.rb +0 -11
  165. data/lib/spec/rails/interop/testcase.rb +0 -14
  166. data/lib/spec/rails/matchers/ar_be_valid.rb +0 -27
  167. data/lib/spec/rails/matchers/assert_select.rb +0 -180
  168. data/lib/spec/rails/matchers/change.rb +0 -13
  169. data/lib/spec/rails/matchers/have_text.rb +0 -57
  170. data/lib/spec/rails/matchers/include_text.rb +0 -54
  171. data/lib/spec/rails/matchers/redirect_to.rb +0 -126
  172. data/lib/spec/rails/matchers/render_template.rb +0 -129
  173. data/lib/spec/rails/matchers/route_to.rb +0 -149
  174. data/lib/spec/rails/matchers.rb +0 -32
  175. data/lib/spec/rails/mocks.rb +0 -136
  176. data/lib/spec/rails/version.rb +0 -16
  177. data/lib/spec/rails.rb +0 -26
  178. data/spec/autotest/mappings_spec.rb +0 -86
  179. data/spec/rails_suite.rb +0 -7
  180. data/spec/resources/controllers/action_view_base_spec_controller.rb +0 -2
  181. data/spec/resources/controllers/application.rb +0 -9
  182. data/spec/resources/controllers/controller_spec_controller.rb +0 -127
  183. data/spec/resources/controllers/example.txt +0 -1
  184. data/spec/resources/controllers/redirect_spec_controller.rb +0 -70
  185. data/spec/resources/controllers/render_spec_controller.rb +0 -34
  186. data/spec/resources/controllers/rjs_spec_controller.rb +0 -58
  187. data/spec/resources/helpers/addition_helper.rb +0 -5
  188. data/spec/resources/helpers/explicit_helper.rb +0 -46
  189. data/spec/resources/helpers/more_explicit_helper.rb +0 -5
  190. data/spec/resources/helpers/plugin_application_helper.rb +0 -6
  191. data/spec/resources/helpers/view_spec_helper.rb +0 -13
  192. data/spec/resources/models/animal.rb +0 -4
  193. data/spec/resources/models/person.rb +0 -18
  194. data/spec/resources/models/thing.rb +0 -3
  195. data/spec/resources/views/controller_spec/_partial.html.erb +0 -0
  196. data/spec/resources/views/controller_spec/action_setting_flash_after_session_reset.html.erb +0 -1
  197. data/spec/resources/views/controller_spec/action_setting_flash_before_session_reset.html.erb +0 -1
  198. data/spec/resources/views/controller_spec/action_setting_the_assigns_hash.html.erb +0 -0
  199. data/spec/resources/views/controller_spec/action_with_errors_in_template.html.erb +0 -1
  200. data/spec/resources/views/controller_spec/action_with_template.html.erb +0 -1
  201. data/spec/resources/views/layouts/application.html.erb +0 -0
  202. data/spec/resources/views/layouts/simple.html.erb +0 -0
  203. data/spec/resources/views/objects/_object.html.erb +0 -1
  204. data/spec/resources/views/render_spec/_a_partial.html.erb +0 -0
  205. data/spec/resources/views/render_spec/action_with_alternate_layout.html.erb +0 -0
  206. data/spec/resources/views/render_spec/some_action.html.erb +0 -0
  207. data/spec/resources/views/render_spec/some_action.js.rjs +0 -1
  208. data/spec/resources/views/rjs_spec/_replacement_partial.html.erb +0 -1
  209. data/spec/resources/views/rjs_spec/hide_div.js.rjs +0 -1
  210. data/spec/resources/views/rjs_spec/hide_page_element.js.rjs +0 -1
  211. data/spec/resources/views/rjs_spec/insert_html.js.rjs +0 -1
  212. data/spec/resources/views/rjs_spec/replace.js.rjs +0 -1
  213. data/spec/resources/views/rjs_spec/replace_html.js.rjs +0 -1
  214. data/spec/resources/views/rjs_spec/replace_html_with_partial.js.rjs +0 -1
  215. data/spec/resources/views/rjs_spec/visual_effect.js.rjs +0 -1
  216. data/spec/resources/views/rjs_spec/visual_toggle_effect.js.rjs +0 -1
  217. data/spec/resources/views/tag_spec/no_tags.html.erb +0 -1
  218. data/spec/resources/views/tag_spec/single_div_with_no_attributes.html.erb +0 -1
  219. data/spec/resources/views/tag_spec/single_div_with_one_attribute.html.erb +0 -1
  220. data/spec/resources/views/view_spec/_partial.html.erb +0 -2
  221. data/spec/resources/views/view_spec/_partial_used_twice.html.erb +0 -0
  222. data/spec/resources/views/view_spec/_partial_with_local_variable.html.erb +0 -1
  223. data/spec/resources/views/view_spec/_partial_with_sub_partial.html.erb +0 -1
  224. data/spec/resources/views/view_spec/_spacer.html.erb +0 -1
  225. data/spec/resources/views/view_spec/accessor.html.erb +0 -6
  226. data/spec/resources/views/view_spec/block_helper.html.erb +0 -3
  227. data/spec/resources/views/view_spec/entry_form.html.erb +0 -2
  228. data/spec/resources/views/view_spec/explicit_helper.html.erb +0 -2
  229. data/spec/resources/views/view_spec/foo/show.html.erb +0 -1
  230. data/spec/resources/views/view_spec/implicit_helper.html.erb +0 -2
  231. data/spec/resources/views/view_spec/multiple_helpers.html.erb +0 -3
  232. data/spec/resources/views/view_spec/path_params.html.erb +0 -1
  233. data/spec/resources/views/view_spec/should_not_receive.html.erb +0 -3
  234. data/spec/resources/views/view_spec/template_with_partial.html.erb +0 -5
  235. data/spec/resources/views/view_spec/template_with_partial_using_collection.html.erb +0 -3
  236. data/spec/resources/views/view_spec/template_with_partial_with_array.html.erb +0 -1
  237. data/spec/resources/views/view_spec/view_helpers.html.erb +0 -1
  238. data/spec/spec/rails/example/assigns_hash_proxy_spec.rb +0 -109
  239. data/spec/spec/rails/example/configuration_spec.rb +0 -65
  240. data/spec/spec/rails/example/controller_example_group_spec.rb +0 -307
  241. data/spec/spec/rails/example/controller_isolation_spec.rb +0 -75
  242. data/spec/spec/rails/example/cookies_proxy_spec.rb +0 -87
  243. data/spec/spec/rails/example/error_handling_spec.rb +0 -90
  244. data/spec/spec/rails/example/example_group_factory_spec.rb +0 -112
  245. data/spec/spec/rails/example/helper_example_group_spec.rb +0 -233
  246. data/spec/spec/rails/example/model_example_group_spec.rb +0 -32
  247. data/spec/spec/rails/example/routing_example_group_spec.rb +0 -10
  248. data/spec/spec/rails/example/shared_routing_example_group_examples.rb +0 -237
  249. data/spec/spec/rails/example/test_unit_assertion_accessibility_spec.rb +0 -33
  250. data/spec/spec/rails/example/view_example_group_spec.rb +0 -346
  251. data/spec/spec/rails/extensions/action_view_base_spec.rb +0 -74
  252. data/spec/spec/rails/extensions/active_record_spec.rb +0 -14
  253. data/spec/spec/rails/interop/testcase_spec.rb +0 -70
  254. data/spec/spec/rails/matchers/ar_be_valid_spec.rb +0 -19
  255. data/spec/spec/rails/matchers/assert_select_spec.rb +0 -835
  256. data/spec/spec/rails/matchers/errors_on_spec.rb +0 -37
  257. data/spec/spec/rails/matchers/have_text_spec.rb +0 -69
  258. data/spec/spec/rails/matchers/include_text_spec.rb +0 -62
  259. data/spec/spec/rails/matchers/redirect_to_spec.rb +0 -253
  260. data/spec/spec/rails/matchers/render_template_spec.rb +0 -208
  261. data/spec/spec/rails/matchers/should_change_spec.rb +0 -15
  262. data/spec/spec/rails/mocks/ar_classes.rb +0 -10
  263. data/spec/spec/rails/mocks/mock_model_spec.rb +0 -109
  264. data/spec/spec/rails/mocks/stub_model_spec.rb +0 -80
  265. data/spec/spec/rails/sample_modified_fixture.rb +0 -8
  266. data/spec/spec/rails/sample_spec.rb +0 -8
  267. data/spec/spec/rails/spec_spec.rb +0 -11
  268. data/spec/spec_helper.rb +0 -78
@@ -0,0 +1,83 @@
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
14
+
15
+ # @private
16
+ def matches?(actual)
17
+ @actual = actual
18
+ actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
19
+ end
20
+
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
27
+
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
44
+
45
+ private
46
+
47
+ def attributes
48
+ @attributes ||= {}
49
+ end
50
+
51
+ def attributes_match?(actual)
52
+ attributes.stringify_keys.all? do |key, value|
53
+ values_match?(value, actual.attributes[key])
54
+ end
55
+ end
56
+
57
+ def unmatched_attributes
58
+ attributes.stringify_keys.reject do |key, value|
59
+ values_match?(value, actual.attributes[key])
60
+ end
61
+ end
62
+ end
63
+
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)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,30 @@
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
9
+
10
+ def failure_message
11
+ "expected #{actual.inspect} to be a new record, but was persisted"
12
+ end
13
+
14
+ def failure_message_when_negated
15
+ "expected #{actual.inspect} to be persisted, but was a new record"
16
+ end
17
+ end
18
+
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
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @private
5
+ class BeValid < RSpec::Matchers::BuiltIn::Be
6
+ def initialize(*args)
7
+ @args = args
8
+ end
9
+
10
+ def matches?(actual)
11
+ @actual = actual
12
+ actual.valid?(*@args)
13
+ end
14
+
15
+ def failure_message
16
+ message = "expected #{actual.inspect} to be valid"
17
+
18
+ if actual.respond_to?(:errors) && actual.method(:errors).arity < 1
19
+ errors = if actual.errors.respond_to?(:full_messages)
20
+ actual.errors.full_messages
21
+ else
22
+ actual.errors
23
+ end
24
+
25
+ message << ", but got errors: #{errors.map(&:to_s).join(', ')}"
26
+ end
27
+
28
+ message
29
+ end
30
+
31
+ def failure_message_when_negated
32
+ "expected #{actual.inspect} not to be valid"
33
+ end
34
+ end
35
+
36
+ # @api public
37
+ # Passes if the given model instance's `valid?` method is true, meaning
38
+ # all of the `ActiveModel::Validations` passed and no errors exist. If a
39
+ # message is not given, a default message is shown listing each error.
40
+ #
41
+ # @example
42
+ # thing = Thing.new
43
+ # expect(thing).to be_valid
44
+ def be_valid(*args)
45
+ BeValid.new(*args)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,258 @@
1
+ # We require the minimum amount of rspec-mocks possible to avoid
2
+ # conflicts with other mocking frameworks.
3
+ # See: https://github.com/rspec/rspec-rails/issues/2252
4
+ require "rspec/mocks/argument_matchers"
5
+ require "rspec/rails/matchers/active_job"
6
+
7
+ # rubocop: disable Metrics/ClassLength
8
+ module RSpec
9
+ module Rails
10
+ module Matchers
11
+ # Matcher class for `have_enqueued_mail`. Should not be instantiated directly.
12
+ #
13
+ # @private
14
+ # @see RSpec::Rails::Matchers#have_enqueued_mail
15
+ class HaveEnqueuedMail < ActiveJob::HaveEnqueuedJob
16
+ MAILER_JOB_METHOD = 'deliver_now'.freeze
17
+
18
+ include RSpec::Mocks::ArgumentMatchers
19
+
20
+ def initialize(mailer_class, method_name)
21
+ super(nil)
22
+ @mailer_class = mailer_class
23
+ @method_name = method_name
24
+ @mail_args = []
25
+ end
26
+
27
+ def description
28
+ "enqueues #{mailer_class_name}.#{@method_name}"
29
+ end
30
+
31
+ def with(*args, &block)
32
+ @mail_args = args
33
+ block.nil? ? super : super(&yield_mail_args(block))
34
+ end
35
+
36
+ def matches?(block)
37
+ raise ArgumentError, 'have_enqueued_mail and enqueue_mail only work with block arguments' unless block.respond_to?(:call)
38
+
39
+ check_active_job_adapter
40
+ super
41
+ end
42
+
43
+ def failure_message
44
+ return @failure_message if defined?(@failure_message)
45
+
46
+ "expected to enqueue #{base_message}".tap do |msg|
47
+ msg << "\n#{unmatching_mail_jobs_message}" if unmatching_mail_jobs.any?
48
+ end
49
+ end
50
+
51
+ def failure_message_when_negated
52
+ "expected not to enqueue #{base_message}"
53
+ end
54
+
55
+ private
56
+
57
+ def base_message
58
+ [mailer_class_name, @method_name].compact.join('.').tap do |msg|
59
+ msg << " #{expected_count_message}"
60
+ msg << " with #{@mail_args}," if @mail_args.any?
61
+ msg << " on queue #{@queue}," if @queue
62
+ msg << " at #{@at.inspect}," if @at
63
+ msg << " but enqueued #{@matching_jobs.size}"
64
+ end
65
+ end
66
+
67
+ def expected_count_message
68
+ "#{message_expectation_modifier} #{@expected_number} #{@expected_number == 1 ? 'time' : 'times'}"
69
+ end
70
+
71
+ def mailer_class_name
72
+ @mailer_class ? @mailer_class.name : 'ActionMailer::Base'
73
+ end
74
+
75
+ def job_matches?(job)
76
+ legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
77
+ end
78
+
79
+ def arguments_match?(job)
80
+ @args =
81
+ if @mail_args.any?
82
+ base_mailer_args + @mail_args
83
+ elsif @mailer_class && @method_name
84
+ base_mailer_args + [any_args]
85
+ elsif @mailer_class
86
+ [mailer_class_name, any_args]
87
+ else
88
+ []
89
+ end
90
+
91
+ super(job)
92
+ end
93
+
94
+ def detect_args_signature_mismatch(jobs)
95
+ return if @method_name.nil?
96
+
97
+ mailer_class = mailer_class_name.constantize
98
+
99
+ jobs.each do |job|
100
+ mailer_args = extract_args_without_parameterized_params(job)
101
+
102
+ if (signature_mismatch = check_args_signature_mismatch(mailer_class, @method_name, mailer_args))
103
+ return signature_mismatch
104
+ end
105
+ end
106
+
107
+ nil
108
+ end
109
+
110
+ def base_mailer_args
111
+ [mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
112
+ end
113
+
114
+ def yield_mail_args(block)
115
+ proc { |*job_args| block.call(*(job_args - base_mailer_args)) }
116
+ end
117
+
118
+ def check_active_job_adapter
119
+ return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
120
+
121
+ raise StandardError, "To use HaveEnqueuedMail matcher set `ActiveJob::Base.queue_adapter = :test`"
122
+ end
123
+
124
+ def unmatching_mail_jobs
125
+ @unmatching_jobs.select do |job|
126
+ job_matches?(job)
127
+ end
128
+ end
129
+
130
+ def unmatching_mail_jobs_message
131
+ msg = "Queued deliveries:"
132
+
133
+ unmatching_mail_jobs.each do |job|
134
+ msg << "\n #{mail_job_message(job)}"
135
+ end
136
+
137
+ msg
138
+ end
139
+
140
+ def mail_job_message(job)
141
+ job_args = deserialize_arguments(job)
142
+
143
+ mailer_method = job_args[0..1].join('.')
144
+ mailer_args = job_args[3..-1]
145
+
146
+ msg_parts = []
147
+ msg_parts << "with #{mailer_args}" if mailer_args.any?
148
+ msg_parts << "on queue #{job[:queue]}" if job[:queue] && job[:queue] != 'mailers'
149
+ msg_parts << "at #{Time.at(job[:at])}" if job[:at]
150
+
151
+ "#{mailer_method} #{msg_parts.join(', ')}".strip
152
+ end
153
+
154
+ # Ruby 3.1 changed how params were serialized on Rails 6.1
155
+ # so we override the active job implementation and customize it here.
156
+ def deserialize_arguments(job)
157
+ args = super
158
+
159
+ return args unless Hash === args.last
160
+
161
+ hash = args.pop
162
+
163
+ if hash.key?("_aj_ruby2_keywords")
164
+ keywords = hash["_aj_ruby2_keywords"]
165
+
166
+ original_hash = keywords.each_with_object({}) { |keyword, new_hash| new_hash[keyword.to_sym] = hash[keyword] }
167
+
168
+ args + [original_hash]
169
+ elsif hash.key?(:args) && hash.key?(:params)
170
+ args + [hash]
171
+ elsif hash.key?(:args)
172
+ args + hash[:args]
173
+ else
174
+ args + [hash]
175
+ end
176
+ end
177
+
178
+ def extract_args_without_parameterized_params(job)
179
+ args = deserialize_arguments(job)
180
+ mailer_args = args - base_mailer_args
181
+
182
+ if parameterized_mail?(job)
183
+ mailer_args = mailer_args[1..-1] # ignore parameterized params
184
+ elsif mailer_args.last.is_a?(Hash) && mailer_args.last.key?(:args)
185
+ mailer_args = args.last[:args]
186
+ end
187
+
188
+ mailer_args
189
+ end
190
+
191
+ def legacy_mail?(job)
192
+ RSpec::Rails::FeatureCheck.has_action_mailer_legacy_delivery_job? && job[:job] <= ActionMailer::DeliveryJob
193
+ end
194
+
195
+ def parameterized_mail?(job)
196
+ RSpec::Rails::FeatureCheck.has_action_mailer_parameterized? && job[:job] <= ActionMailer::Parameterized::DeliveryJob
197
+ end
198
+
199
+ def unified_mail?(job)
200
+ RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob
201
+ end
202
+ end
203
+
204
+ # @api public
205
+ # Passes if an email has been enqueued inside block.
206
+ # May chain with to specify expected arguments.
207
+ # May chain at_least, at_most or exactly to specify a number of times.
208
+ # May chain at to specify a send time.
209
+ # May chain on_queue to specify a queue.
210
+ #
211
+ # @example
212
+ # expect {
213
+ # MyMailer.welcome(user).deliver_later
214
+ # }.to have_enqueued_mail
215
+ #
216
+ # expect {
217
+ # MyMailer.welcome(user).deliver_later
218
+ # }.to have_enqueued_mail(MyMailer)
219
+ #
220
+ # expect {
221
+ # MyMailer.welcome(user).deliver_later
222
+ # }.to have_enqueued_mail(MyMailer, :welcome)
223
+ #
224
+ # # Using alias
225
+ # expect {
226
+ # MyMailer.welcome(user).deliver_later
227
+ # }.to enqueue_mail(MyMailer, :welcome)
228
+ #
229
+ # expect {
230
+ # MyMailer.welcome(user).deliver_later
231
+ # }.to have_enqueued_mail(MyMailer, :welcome).with(user)
232
+ #
233
+ # expect {
234
+ # MyMailer.welcome(user).deliver_later
235
+ # MyMailer.welcome(user).deliver_later
236
+ # }.to have_enqueued_mail(MyMailer, :welcome).at_least(:once)
237
+ #
238
+ # expect {
239
+ # MyMailer.welcome(user).deliver_later
240
+ # }.to have_enqueued_mail(MyMailer, :welcome).at_most(:twice)
241
+ #
242
+ # expect {
243
+ # MyMailer.welcome(user).deliver_later(wait_until: Date.tomorrow.noon)
244
+ # }.to have_enqueued_mail(MyMailer, :welcome).at(Date.tomorrow.noon)
245
+ #
246
+ # expect {
247
+ # MyMailer.welcome(user).deliver_later(queue: :urgent_mail)
248
+ # }.to have_enqueued_mail(MyMailer, :welcome).on_queue(:urgent_mail)
249
+ def have_enqueued_mail(mailer_class = nil, mail_method_name = nil)
250
+ HaveEnqueuedMail.new(mailer_class, mail_method_name)
251
+ end
252
+ alias_method :have_enqueued_email, :have_enqueued_mail
253
+ alias_method :enqueue_mail, :have_enqueued_mail
254
+ alias_method :enqueue_email, :have_enqueued_mail
255
+ end
256
+ end
257
+ end
258
+ # rubocop: enable Metrics/ClassLength