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,517 @@
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 Metrics/ClassLength
12
+ # @private
13
+ class Base < RSpec::Rails::Matchers::BaseMatcher
14
+ def initialize
15
+ @args = []
16
+ @queue = nil
17
+ @priority = nil
18
+ @at = nil
19
+ @block = proc { }
20
+ set_expected_number(:exactly, 1)
21
+ end
22
+
23
+ def with(*args, &block)
24
+ @args = args
25
+ @block = block if block.present?
26
+ self
27
+ end
28
+
29
+ def on_queue(queue)
30
+ @queue = queue.to_s
31
+ self
32
+ end
33
+
34
+ def at_priority(priority)
35
+ @priority = priority.to_i
36
+ self
37
+ end
38
+
39
+ def at(time_or_date)
40
+ case time_or_date
41
+ when Time then @at = Time.at(time_or_date.to_f)
42
+ else
43
+ @at = time_or_date
44
+ end
45
+ self
46
+ end
47
+
48
+ def exactly(count)
49
+ set_expected_number(:exactly, count)
50
+ self
51
+ end
52
+
53
+ def at_least(count)
54
+ set_expected_number(:at_least, count)
55
+ self
56
+ end
57
+
58
+ def at_most(count)
59
+ set_expected_number(:at_most, count)
60
+ self
61
+ end
62
+
63
+ def times
64
+ self
65
+ end
66
+
67
+ def once
68
+ exactly(:once)
69
+ end
70
+
71
+ def twice
72
+ exactly(:twice)
73
+ end
74
+
75
+ def thrice
76
+ exactly(:thrice)
77
+ end
78
+
79
+ def failure_message
80
+ return @failure_message if defined?(@failure_message)
81
+
82
+ "expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
83
+ if @unmatching_jobs.any?
84
+ msg << "\nQueued jobs:"
85
+ @unmatching_jobs.each do |job|
86
+ msg << "\n #{base_job_message(job)}"
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def failure_message_when_negated
93
+ "expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
94
+ end
95
+
96
+ def message_expectation_modifier
97
+ case @expectation_type
98
+ when :exactly then "exactly"
99
+ when :at_most then "at most"
100
+ when :at_least then "at least"
101
+ end
102
+ end
103
+
104
+ def supports_block_expectations?
105
+ true
106
+ end
107
+
108
+ private
109
+
110
+ def check(jobs)
111
+ @matching_jobs, @unmatching_jobs = jobs.partition do |job|
112
+ if matches_constraints?(job)
113
+ args = deserialize_arguments(job)
114
+ @block.call(*args)
115
+ true
116
+ else
117
+ false
118
+ end
119
+ end
120
+
121
+ if (signature_mismatch = detect_args_signature_mismatch(@matching_jobs))
122
+ @failure_message = signature_mismatch
123
+ return false
124
+ end
125
+
126
+ @matching_jobs_count = @matching_jobs.size
127
+
128
+ case @expectation_type
129
+ when :exactly then @expected_number == @matching_jobs_count
130
+ when :at_most then @expected_number >= @matching_jobs_count
131
+ when :at_least then @expected_number <= @matching_jobs_count
132
+ end
133
+ end
134
+
135
+ def base_message
136
+ "#{message_expectation_modifier} #{@expected_number} jobs,".tap do |msg|
137
+ msg << " with #{@args}," if @args.any?
138
+ msg << " on queue #{@queue}," if @queue
139
+ msg << " at #{@at.inspect}," if @at
140
+ msg << " with priority #{@priority}," if @priority
141
+ msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
142
+ end
143
+ end
144
+
145
+ def base_job_message(job)
146
+ msg_parts = []
147
+ msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
148
+ msg_parts << "on queue #{job[:queue]}" if job[:queue]
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
156
+
157
+ "#{job[:job].name} job".tap do |msg|
158
+ msg << " #{msg_parts.join(', ')}" if msg_parts.any?
159
+ end
160
+ end
161
+
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)
167
+ @job ? @job == job[:job] : true
168
+ end
169
+
170
+ def arguments_match?(job)
171
+ if @args.any?
172
+ args = serialize_and_deserialize_arguments(@args)
173
+ deserialized_args = deserialize_arguments(job)
174
+ RSpec::Mocks::ArgumentListMatcher.new(*args).args_match?(*deserialized_args)
175
+ else
176
+ true
177
+ end
178
+ end
179
+
180
+ def detect_args_signature_mismatch(jobs)
181
+ jobs.each do |job|
182
+ args = deserialize_arguments(job)
183
+
184
+ if (signature_mismatch = check_args_signature_mismatch(job.fetch(:job), :perform, args))
185
+ return signature_mismatch
186
+ end
187
+ end
188
+
189
+ nil
190
+ end
191
+
192
+ def check_args_signature_mismatch(job_class, job_method, args)
193
+ signature = Support::MethodSignature.new(job_class.public_instance_method(job_method))
194
+ verifier = Support::StrictSignatureVerifier.new(signature, args)
195
+
196
+ unless verifier.valid?
197
+ "Incorrect arguments passed to #{job_class.name}: #{verifier.error_message}"
198
+ end
199
+ end
200
+
201
+ def queue_match?(job)
202
+ return true unless @queue
203
+
204
+ @queue == job[:queue]
205
+ end
206
+
207
+ def priority_match?(job)
208
+ return true unless @priority
209
+
210
+ @priority == job[:priority]
211
+ end
212
+
213
+ def at_match?(job)
214
+ return true unless @at
215
+ return job[:at].nil? if @at == :no_wait
216
+ return false unless job[:at]
217
+
218
+ scheduled_at = Time.at(job[:at])
219
+ values_match?(@at, scheduled_at) || check_for_inprecise_value(scheduled_at)
220
+ end
221
+
222
+ def check_for_inprecise_value(scheduled_at)
223
+ return unless Time === @at && values_match?(@at.change(usec: 0), scheduled_at)
224
+
225
+ RSpec.warn_with((<<-WARNING).gsub(/^\s+\|/, '').chomp)
226
+ |[WARNING] Your expected `at(...)` value does not match the job scheduled_at value
227
+ |unless microseconds are removed. This precision error often occurs when checking
228
+ |values against `Time.current` / `Time.now` which have usec precision, but Rails
229
+ |uses `n.seconds.from_now` internally which has a usec count of `0`.
230
+ |
231
+ |Use `change(usec: 0)` to correct these values. For example:
232
+ |
233
+ |`Time.current.change(usec: 0)`
234
+ |
235
+ |Note: RSpec cannot do this for you because jobs can be scheduled with usec
236
+ |precision and we do not know whether it is on purpose or not.
237
+ |
238
+ |
239
+ WARNING
240
+ false
241
+ end
242
+
243
+ def set_expected_number(relativity, count)
244
+ @expectation_type = relativity
245
+ @expected_number = case count
246
+ when :once then 1
247
+ when :twice then 2
248
+ when :thrice then 3
249
+ else Integer(count)
250
+ end
251
+ end
252
+
253
+ def serialize_and_deserialize_arguments(args)
254
+ serialized = ::ActiveJob::Arguments.serialize(args)
255
+ ::ActiveJob::Arguments.deserialize(serialized)
256
+ rescue ::ActiveJob::SerializationError
257
+ args
258
+ end
259
+
260
+ def deserialize_arguments(job)
261
+ ::ActiveJob::Arguments.deserialize(job[:args])
262
+ rescue ::ActiveJob::DeserializationError
263
+ job[:args]
264
+ end
265
+
266
+ def queue_adapter
267
+ ::ActiveJob::Base.queue_adapter
268
+ end
269
+ end
270
+ # rubocop: enable Metrics/ClassLength
271
+
272
+ # @private
273
+ class HaveEnqueuedJob < Base
274
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
275
+ MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
276
+
277
+ def initialize(job)
278
+ super()
279
+ @job = job
280
+ end
281
+
282
+ def matches?(proc)
283
+ raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc
284
+
285
+ original_enqueued_jobs = Set.new(queue_adapter.enqueued_jobs)
286
+ proc.call
287
+ enqueued_jobs = Set.new(queue_adapter.enqueued_jobs)
288
+
289
+ check(enqueued_jobs - original_enqueued_jobs)
290
+ end
291
+
292
+ def does_not_match?(proc)
293
+ set_expected_number(:at_least, 1)
294
+
295
+ !matches?(proc)
296
+ end
297
+ end
298
+
299
+ # @private
300
+ class HaveBeenEnqueued < Base
301
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
302
+ MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
303
+
304
+ def matches?(job)
305
+ @job = job
306
+ check(queue_adapter.enqueued_jobs)
307
+ end
308
+
309
+ def does_not_match?(proc)
310
+ set_expected_number(:at_least, 1)
311
+
312
+ !matches?(proc)
313
+ end
314
+ end
315
+
316
+ # @private
317
+ class HavePerformedJob < Base
318
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
319
+ MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
320
+
321
+ def initialize(job)
322
+ super()
323
+ @job = job
324
+ end
325
+
326
+ def matches?(proc)
327
+ raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc
328
+
329
+ original_performed_jobs_count = queue_adapter.performed_jobs.count
330
+ proc.call
331
+ in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)
332
+
333
+ check(in_block_jobs)
334
+ end
335
+ end
336
+
337
+ # @private
338
+ class HaveBeenPerformed < Base
339
+ FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
340
+ MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
341
+
342
+ def matches?(job)
343
+ @job = job
344
+ check(queue_adapter.performed_jobs)
345
+ end
346
+ end
347
+ end
348
+
349
+ # @api public
350
+ # Passes if a job has been enqueued inside block. May chain at_least, at_most or exactly to specify a number of times.
351
+ #
352
+ # @example
353
+ # expect {
354
+ # HeavyLiftingJob.perform_later
355
+ # }.to have_enqueued_job
356
+ #
357
+ # # Using alias
358
+ # expect {
359
+ # HeavyLiftingJob.perform_later
360
+ # }.to enqueue_job
361
+ #
362
+ # expect {
363
+ # HelloJob.perform_later
364
+ # HeavyLiftingJob.perform_later
365
+ # }.to have_enqueued_job(HelloJob).exactly(:once)
366
+ #
367
+ # expect {
368
+ # 3.times { HelloJob.perform_later }
369
+ # }.to have_enqueued_job(HelloJob).at_least(2).times
370
+ #
371
+ # expect {
372
+ # HelloJob.perform_later
373
+ # }.to have_enqueued_job(HelloJob).at_most(:twice)
374
+ #
375
+ # expect {
376
+ # HelloJob.perform_later
377
+ # HeavyLiftingJob.perform_later
378
+ # }.to have_enqueued_job(HelloJob).and have_enqueued_job(HeavyLiftingJob)
379
+ #
380
+ # expect {
381
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
382
+ # }.to have_enqueued_job.with(42).on_queue("low").at(Date.tomorrow.noon)
383
+ #
384
+ # expect {
385
+ # HelloJob.set(queue: "low").perform_later(42)
386
+ # }.to have_enqueued_job.with(42).on_queue("low").at(:no_wait)
387
+ #
388
+ # expect {
389
+ # HelloJob.perform_later('rspec_rails', 'rails', 42)
390
+ # }.to have_enqueued_job.with { |from, to, times|
391
+ # # Perform more complex argument matching using dynamic arguments
392
+ # expect(from).to include "_#{to}"
393
+ # }
394
+ def have_enqueued_job(job = nil)
395
+ check_active_job_adapter
396
+ ActiveJob::HaveEnqueuedJob.new(job)
397
+ end
398
+ alias_method :enqueue_job, :have_enqueued_job
399
+
400
+ # @api public
401
+ # Passes if a job has been enqueued. May chain at_least, at_most or exactly to specify a number of times.
402
+ #
403
+ # @example
404
+ # before { ActiveJob::Base.queue_adapter.enqueued_jobs.clear }
405
+ #
406
+ # HeavyLiftingJob.perform_later
407
+ # expect(HeavyLiftingJob).to have_been_enqueued
408
+ #
409
+ # HelloJob.perform_later
410
+ # HeavyLiftingJob.perform_later
411
+ # expect(HeavyLiftingJob).to have_been_enqueued.exactly(:once)
412
+ #
413
+ # 3.times { HelloJob.perform_later }
414
+ # expect(HelloJob).to have_been_enqueued.at_least(2).times
415
+ #
416
+ # HelloJob.perform_later
417
+ # expect(HelloJob).to enqueue_job(HelloJob).at_most(:twice)
418
+ #
419
+ # HelloJob.perform_later
420
+ # HeavyLiftingJob.perform_later
421
+ # expect(HelloJob).to have_been_enqueued
422
+ # expect(HeavyLiftingJob).to have_been_enqueued
423
+ #
424
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
425
+ # expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(Date.tomorrow.noon)
426
+ #
427
+ # HelloJob.set(queue: "low").perform_later(42)
428
+ # expect(HelloJob).to have_been_enqueued.with(42).on_queue("low").at(:no_wait)
429
+ def have_been_enqueued
430
+ check_active_job_adapter
431
+ ActiveJob::HaveBeenEnqueued.new
432
+ end
433
+
434
+ # @api public
435
+ # Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
436
+ #
437
+ # @example
438
+ # expect {
439
+ # perform_enqueued_jobs { HeavyLiftingJob.perform_later }
440
+ # }.to have_performed_job
441
+ #
442
+ # expect {
443
+ # perform_enqueued_jobs {
444
+ # HelloJob.perform_later
445
+ # HeavyLiftingJob.perform_later
446
+ # }
447
+ # }.to have_performed_job(HelloJob).exactly(:once)
448
+ #
449
+ # expect {
450
+ # perform_enqueued_jobs { 3.times { HelloJob.perform_later } }
451
+ # }.to have_performed_job(HelloJob).at_least(2).times
452
+ #
453
+ # expect {
454
+ # perform_enqueued_jobs { HelloJob.perform_later }
455
+ # }.to have_performed_job(HelloJob).at_most(:twice)
456
+ #
457
+ # expect {
458
+ # perform_enqueued_jobs {
459
+ # HelloJob.perform_later
460
+ # HeavyLiftingJob.perform_later
461
+ # }
462
+ # }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
463
+ #
464
+ # expect {
465
+ # perform_enqueued_jobs {
466
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
467
+ # }
468
+ # }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
469
+ def have_performed_job(job = nil)
470
+ check_active_job_adapter
471
+ ActiveJob::HavePerformedJob.new(job)
472
+ end
473
+ alias_method :perform_job, :have_performed_job
474
+
475
+ # @api public
476
+ # Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
477
+ #
478
+ # @example
479
+ # before do
480
+ # ActiveJob::Base.queue_adapter.performed_jobs.clear
481
+ # ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
482
+ # ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
483
+ # end
484
+ #
485
+ # HeavyLiftingJob.perform_later
486
+ # expect(HeavyLiftingJob).to have_been_performed
487
+ #
488
+ # HelloJob.perform_later
489
+ # HeavyLiftingJob.perform_later
490
+ # expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
491
+ #
492
+ # 3.times { HelloJob.perform_later }
493
+ # expect(HelloJob).to have_been_performed.at_least(2).times
494
+ #
495
+ # HelloJob.perform_later
496
+ # HeavyLiftingJob.perform_later
497
+ # expect(HelloJob).to have_been_performed
498
+ # expect(HeavyLiftingJob).to have_been_performed
499
+ #
500
+ # HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
501
+ # expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
502
+ def have_been_performed
503
+ check_active_job_adapter
504
+ ActiveJob::HaveBeenPerformed.new
505
+ end
506
+
507
+ private
508
+
509
+ # @private
510
+ def check_active_job_adapter
511
+ return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
512
+
513
+ raise StandardError, "To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`"
514
+ end
515
+ end
516
+ end
517
+ end
@@ -0,0 +1,179 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @api private
5
+ #
6
+ # Base class to build matchers. Should not be instantiated directly.
7
+ class BaseMatcher
8
+ include RSpec::Matchers::Composable
9
+
10
+ # @api private
11
+ # Used to detect when no arg is passed to `initialize`.
12
+ # `nil` cannot be used because it's a valid value to pass.
13
+ UNDEFINED = Object.new.freeze
14
+
15
+ # @private
16
+ attr_reader :actual, :expected, :rescued_exception
17
+
18
+ # @private
19
+ attr_writer :matcher_name
20
+
21
+ def initialize(expected = UNDEFINED)
22
+ @expected = expected unless UNDEFINED.equal?(expected)
23
+ end
24
+
25
+ # @api private
26
+ # Indicates if the match is successful. Delegates to `match`, which
27
+ # should be defined on a subclass. Takes care of consistently
28
+ # initializing the `actual` attribute.
29
+ def matches?(actual)
30
+ @actual = actual
31
+ match(expected, actual)
32
+ end
33
+
34
+ # @api private
35
+ # Used to wrap a block of code that will indicate failure by
36
+ # raising one of the named exceptions.
37
+ #
38
+ # This is used by rspec-rails for some of its matchers that
39
+ # wrap rails' assertions.
40
+ def match_unless_raises(*exceptions)
41
+ exceptions.unshift Exception if exceptions.empty?
42
+ begin
43
+ yield
44
+ true
45
+ rescue *exceptions => @rescued_exception
46
+ false
47
+ end
48
+ end
49
+
50
+ # @api private
51
+ # Generates a description using {RSpec::Matchers::EnglishPhrasing}.
52
+ # @return [String]
53
+ def description
54
+ desc = RSpec::Matchers::EnglishPhrasing.split_words(self.class.matcher_name)
55
+ desc << RSpec::Matchers::EnglishPhrasing.list(@expected) if defined?(@expected)
56
+ desc
57
+ end
58
+
59
+ # @api private
60
+ # Matchers are not diffable by default. Override this to make your
61
+ # subclass diffable.
62
+ def diffable?
63
+ false
64
+ end
65
+
66
+ # @api private
67
+ # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
68
+ # rather than block matchers (i.e. meant to work with `expect { }`), so
69
+ # this defaults to false. Block matchers must override this to return true.
70
+ def supports_block_expectations?
71
+ false
72
+ end
73
+
74
+ # @api private
75
+ def expects_call_stack_jump?
76
+ false
77
+ end
78
+
79
+ # @private
80
+ def expected_formatted
81
+ RSpec::Support::ObjectFormatter.format(@expected)
82
+ end
83
+
84
+ # @private
85
+ def actual_formatted
86
+ RSpec::Support::ObjectFormatter.format(@actual)
87
+ end
88
+
89
+ # @private
90
+ def self.matcher_name
91
+ @matcher_name ||= underscore(name.split('::').last)
92
+ end
93
+
94
+ # @private
95
+ def matcher_name
96
+ if defined?(@matcher_name)
97
+ @matcher_name
98
+ else
99
+ self.class.matcher_name
100
+ end
101
+ end
102
+
103
+ # @private
104
+ # Borrowed from ActiveSupport.
105
+ def self.underscore(camel_cased_word)
106
+ word = camel_cased_word.to_s.dup
107
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
108
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
109
+ word.tr!('-', '_')
110
+ word.downcase!
111
+ word
112
+ end
113
+ private_class_method :underscore
114
+
115
+ private
116
+
117
+ def assert_ivars(*expected_ivars)
118
+ return unless (expected_ivars - present_ivars).any?
119
+
120
+ ivar_list = RSpec::Matchers::EnglishPhrasing.list(expected_ivars)
121
+ raise "#{self.class.name} needs to supply#{ivar_list}"
122
+ end
123
+
124
+ alias present_ivars instance_variables
125
+
126
+ # @private
127
+ module HashFormatting
128
+ # `{ :a => 5, :b => 2 }.inspect` produces:
129
+ #
130
+ # {:a=>5, :b=>2}
131
+ #
132
+ # ...but it looks much better as:
133
+ #
134
+ # {:a => 5, :b => 2}
135
+ #
136
+ # This is idempotent and safe to run on a string multiple times.
137
+ def improve_hash_formatting(inspect_string)
138
+ inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
139
+ end
140
+ module_function :improve_hash_formatting
141
+ end
142
+
143
+ include HashFormatting
144
+
145
+ # @api private
146
+ # Provides default implementations of failure messages, based on the `description`.
147
+ module DefaultFailureMessages
148
+ # @api private
149
+ # Provides a good generic failure message. Based on `description`.
150
+ # When subclassing, if you are not satisfied with this failure message
151
+ # you often only need to override `description`.
152
+ # @return [String]
153
+ def failure_message
154
+ "expected #{description_of @actual} to #{description}".dup
155
+ end
156
+
157
+ # @api private
158
+ # Provides a good generic negative failure message. Based on `description`.
159
+ # When subclassing, if you are not satisfied with this failure message
160
+ # you often only need to override `description`.
161
+ # @return [String]
162
+ def failure_message_when_negated
163
+ "expected #{description_of @actual} not to #{description}".dup
164
+ end
165
+
166
+ # @private
167
+ def self.has_default_failure_messages?(matcher)
168
+ matcher.method(:failure_message).owner == self &&
169
+ matcher.method(:failure_message_when_negated).owner == self
170
+ rescue NameError
171
+ false
172
+ end
173
+ end
174
+
175
+ include DefaultFailureMessages
176
+ end
177
+ end
178
+ end
179
+ end