rspec-rails 3.0.2 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +3 -1
  5. data/Capybara.md +6 -55
  6. data/Changelog.md +805 -47
  7. data/{License.txt → LICENSE.md} +5 -3
  8. data/README.md +278 -444
  9. data/lib/generators/rspec/channel/channel_generator.rb +12 -0
  10. data/lib/generators/rspec/{observer/templates/observer_spec.rb → channel/templates/channel_spec.rb.erb} +1 -1
  11. data/lib/generators/rspec/controller/controller_generator.rb +24 -7
  12. data/lib/generators/rspec/controller/templates/controller_spec.rb +3 -3
  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 +1 -1
  16. data/lib/generators/rspec/feature/feature_generator.rb +15 -2
  17. data/lib/generators/rspec/feature/templates/feature_singular_spec.rb +5 -0
  18. data/lib/generators/rspec/feature/templates/feature_spec.rb +1 -1
  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 +2 -2
  22. data/lib/generators/rspec/helper/templates/helper_spec.rb +1 -1
  23. data/lib/generators/rspec/install/install_generator.rb +41 -7
  24. data/lib/generators/rspec/install/templates/spec/rails_helper.rb +63 -22
  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 +12 -3
  30. data/lib/generators/rspec/mailer/templates/mailer_spec.rb +2 -2
  31. data/lib/generators/rspec/mailer/templates/preview.rb +13 -0
  32. data/lib/generators/rspec/model/model_generator.rb +20 -6
  33. data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
  34. data/lib/generators/rspec/model/templates/model_spec.rb +1 -1
  35. data/lib/generators/rspec/request/request_generator.rb +17 -0
  36. data/lib/generators/rspec/request/templates/request_spec.rb +10 -0
  37. data/lib/generators/rspec/scaffold/scaffold_generator.rb +90 -113
  38. data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +129 -0
  39. data/lib/generators/rspec/scaffold/templates/api_request_spec.rb +131 -0
  40. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +46 -64
  41. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +11 -7
  42. data/lib/generators/rspec/scaffold/templates/index_spec.rb +4 -3
  43. data/lib/generators/rspec/scaffold/templates/new_spec.rb +4 -4
  44. data/lib/generators/rspec/scaffold/templates/request_spec.rb +138 -0
  45. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +18 -11
  46. data/lib/generators/rspec/scaffold/templates/show_spec.rb +3 -3
  47. data/lib/generators/rspec/system/system_generator.rb +24 -0
  48. data/lib/generators/rspec/system/templates/system_spec.rb +9 -0
  49. data/lib/generators/rspec/view/templates/view_spec.rb +1 -1
  50. data/lib/generators/rspec/view/view_generator.rb +4 -4
  51. data/lib/generators/rspec.rb +30 -11
  52. data/lib/rspec/rails/active_record.rb +25 -0
  53. data/lib/rspec/rails/adapters.rb +46 -29
  54. data/lib/rspec/rails/configuration.rb +165 -41
  55. data/lib/rspec/rails/example/channel_example_group.rb +93 -0
  56. data/lib/rspec/rails/example/controller_example_group.rb +185 -149
  57. data/lib/rspec/rails/example/feature_example_group.rb +43 -23
  58. data/lib/rspec/rails/example/helper_example_group.rb +28 -25
  59. data/lib/rspec/rails/example/job_example_group.rb +23 -0
  60. data/lib/rspec/rails/example/mailbox_example_group.rb +80 -0
  61. data/lib/rspec/rails/example/mailer_example_group.rb +27 -22
  62. data/lib/rspec/rails/example/model_example_group.rb +9 -6
  63. data/lib/rspec/rails/example/rails_example_group.rb +9 -2
  64. data/lib/rspec/rails/example/request_example_group.rb +21 -17
  65. data/lib/rspec/rails/example/routing_example_group.rb +47 -39
  66. data/lib/rspec/rails/example/system_example_group.rb +180 -0
  67. data/lib/rspec/rails/example/view_example_group.rb +179 -134
  68. data/lib/rspec/rails/example.rb +4 -0
  69. data/lib/rspec/rails/extensions/active_record/proxy.rb +5 -11
  70. data/lib/rspec/rails/feature_check.rb +51 -0
  71. data/lib/rspec/rails/file_fixture_support.rb +18 -0
  72. data/lib/rspec/rails/fixture_file_upload_support.rb +45 -0
  73. data/lib/rspec/rails/fixture_support.rb +70 -14
  74. data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +180 -0
  75. data/lib/rspec/rails/matchers/action_cable/have_streams.rb +58 -0
  76. data/lib/rspec/rails/matchers/action_cable.rb +70 -0
  77. data/lib/rspec/rails/matchers/action_mailbox.rb +73 -0
  78. data/lib/rspec/rails/matchers/active_job.rb +526 -0
  79. data/lib/rspec/rails/matchers/base_matcher.rb +179 -0
  80. data/lib/rspec/rails/matchers/be_a_new.rb +70 -64
  81. data/lib/rspec/rails/matchers/be_new_record.rb +25 -20
  82. data/lib/rspec/rails/matchers/be_valid.rb +39 -34
  83. data/lib/rspec/rails/matchers/have_enqueued_mail.rb +259 -0
  84. data/lib/rspec/rails/matchers/have_http_status.rb +359 -333
  85. data/lib/rspec/rails/matchers/have_rendered.rb +55 -32
  86. data/lib/rspec/rails/matchers/redirect_to.rb +30 -27
  87. data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
  88. data/lib/rspec/rails/matchers/routing_matchers.rb +107 -101
  89. data/lib/rspec/rails/matchers/send_email.rb +122 -0
  90. data/lib/rspec/rails/matchers.rb +21 -12
  91. data/lib/rspec/rails/tasks/rspec.rake +9 -17
  92. data/lib/rspec/rails/vendor/capybara.rb +10 -11
  93. data/lib/rspec/rails/version.rb +1 -1
  94. data/lib/rspec/rails/view_assigns.rb +1 -20
  95. data/lib/rspec/rails/view_path_builder.rb +29 -0
  96. data/lib/rspec/rails/view_rendering.rb +89 -27
  97. data/lib/rspec/rails/view_spec_methods.rb +56 -0
  98. data/lib/rspec/rails.rb +9 -1
  99. data/lib/rspec-rails.rb +83 -3
  100. data.tar.gz.sig +0 -0
  101. metadata +108 -78
  102. metadata.gz.sig +3 -2
  103. data/lib/generators/rspec/integration/integration_generator.rb +0 -17
  104. data/lib/generators/rspec/integration/templates/request_spec.rb +0 -10
  105. data/lib/generators/rspec/observer/observer_generator.rb +0 -13
@@ -0,0 +1,180 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ module ActionCable
5
+ # rubocop: disable Metrics/ClassLength
6
+ # @private
7
+ class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
8
+ def initialize(target, channel:)
9
+ @target = target
10
+ @channel = channel
11
+ @block = proc { }
12
+ @data = nil
13
+ set_expected_number(:exactly, 1)
14
+ end
15
+
16
+ def with(data = nil, &block)
17
+ @data = data
18
+ @data = @data.with_indifferent_access if @data.is_a?(Hash)
19
+ @block = block if block
20
+ self
21
+ end
22
+
23
+ def exactly(count)
24
+ set_expected_number(:exactly, count)
25
+ self
26
+ end
27
+
28
+ def at_least(count)
29
+ set_expected_number(:at_least, count)
30
+ self
31
+ end
32
+
33
+ def at_most(count)
34
+ set_expected_number(:at_most, count)
35
+ self
36
+ end
37
+
38
+ def times
39
+ self
40
+ end
41
+
42
+ def once
43
+ exactly(:once)
44
+ end
45
+
46
+ def twice
47
+ exactly(:twice)
48
+ end
49
+
50
+ def thrice
51
+ exactly(:thrice)
52
+ end
53
+
54
+ def description
55
+ "have broadcasted #{base_description}"
56
+ end
57
+
58
+ def failure_message
59
+ "expected to broadcast #{base_message}".tap do |msg|
60
+ if @unmatching_msgs.any?
61
+ msg << "\nBroadcasted messages to #{stream}:"
62
+ @unmatching_msgs.each do |data|
63
+ msg << "\n #{data}"
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def failure_message_when_negated
70
+ "expected not to broadcast #{base_message}"
71
+ end
72
+
73
+ def message_expectation_modifier
74
+ case @expectation_type
75
+ when :exactly then "exactly"
76
+ when :at_most then "at most"
77
+ when :at_least then "at least"
78
+ end
79
+ end
80
+
81
+ def supports_block_expectations?
82
+ true
83
+ end
84
+
85
+ def matches?(proc)
86
+ raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
87
+
88
+ original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
89
+ proc.call
90
+ in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)
91
+
92
+ check(in_block_messages)
93
+ end
94
+
95
+ def from_channel(channel)
96
+ @channel = channel
97
+ self
98
+ end
99
+
100
+ private
101
+
102
+ def stream
103
+ @stream ||= case @target
104
+ when String
105
+ @target
106
+ when Symbol
107
+ @target.to_s
108
+ else
109
+ check_channel_presence
110
+ @channel.broadcasting_for(@target)
111
+ end
112
+ end
113
+
114
+ def check(messages)
115
+ @matching_msgs, @unmatching_msgs = messages.partition do |msg|
116
+ decoded = ActiveSupport::JSON.decode(msg)
117
+ decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
118
+
119
+ if @data.nil? || values_match?(@data, decoded)
120
+ @block.call(decoded)
121
+ true
122
+ else
123
+ false
124
+ end
125
+ end
126
+
127
+ @matching_msgs_count = @matching_msgs.size
128
+
129
+ case @expectation_type
130
+ when :exactly then @expected_number == @matching_msgs_count
131
+ when :at_most then @expected_number >= @matching_msgs_count
132
+ when :at_least then @expected_number <= @matching_msgs_count
133
+ end
134
+ end
135
+
136
+ def set_expected_number(relativity, count)
137
+ @expectation_type = relativity
138
+ @expected_number =
139
+ case count
140
+ when :once then 1
141
+ when :twice then 2
142
+ when :thrice then 3
143
+ else Integer(count)
144
+ end
145
+ end
146
+
147
+ def base_description
148
+ "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
149
+ msg << " with #{data_description(@data)}" unless @data.nil?
150
+ end
151
+ end
152
+
153
+ def base_message
154
+ "#{base_description}, but broadcast #{@matching_msgs_count}"
155
+ end
156
+
157
+ def data_description(data)
158
+ if data.is_a?(RSpec::Matchers::Composable)
159
+ data.description
160
+ else
161
+ data.inspect
162
+ end
163
+ end
164
+
165
+ def pubsub_adapter
166
+ ::ActionCable.server.pubsub
167
+ end
168
+
169
+ def check_channel_presence
170
+ return if @channel.present? && @channel.respond_to?(:channel_name)
171
+
172
+ error_msg = "Broadcasting channel can't be inferred. Please, specify it with `from_channel`"
173
+ raise ArgumentError, error_msg
174
+ end
175
+ end
176
+ # rubocop: enable Metrics/ClassLength
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,58 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ module ActionCable
5
+ # @api private
6
+ # Provides the implementation for `have_stream`, `have_stream_for`, and `have_stream_from`.
7
+ # Not intended to be instantiated directly.
8
+ class HaveStream < RSpec::Matchers::BuiltIn::BaseMatcher
9
+ # @api private
10
+ # @return [String]
11
+ def failure_message
12
+ "expected to have #{base_message}"
13
+ end
14
+
15
+ # @api private
16
+ # @return [String]
17
+ def failure_message_when_negated
18
+ "expected not to have #{base_message}"
19
+ end
20
+
21
+ # @api private
22
+ # @return [Boolean]
23
+ def matches?(subscription)
24
+ raise(ArgumentError, "have_streams is used for negated expectations only") if no_expected?
25
+
26
+ match(subscription)
27
+ end
28
+
29
+ # @api private
30
+ # @return [Boolean]
31
+ def does_not_match?(subscription)
32
+ !match(subscription)
33
+ end
34
+
35
+ private
36
+
37
+ def match(subscription)
38
+ case subscription
39
+ when ::ActionCable::Channel::Base
40
+ @actual = subscription.streams
41
+ no_expected? ? actual.any? : actual.any? { |i| expected === i }
42
+ else
43
+ raise ArgumentError, "have_stream, have_stream_from and have_stream_from support expectations on subscription only"
44
+ end
45
+ end
46
+
47
+ def base_message
48
+ no_expected? ? "any stream started" : "stream #{expected_formatted} started, but have #{actual_formatted}"
49
+ end
50
+
51
+ def no_expected?
52
+ !defined?(@expected)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,70 @@
1
+ require "rspec/rails/matchers/action_cable/have_broadcasted_to"
2
+
3
+ module RSpec
4
+ module Rails
5
+ module Matchers
6
+ extend RSpec::Matchers::DSL
7
+
8
+ # Namespace for various implementations of ActionCable features
9
+ #
10
+ # @api private
11
+ module ActionCable
12
+ end
13
+
14
+ # @api public
15
+ # Passes if a message has been sent to a stream/object inside a block.
16
+ # May chain `at_least`, `at_most` or `exactly` to specify a number of times.
17
+ # To specify channel from which message has been broadcasted to object use `from_channel`.
18
+ #
19
+ #
20
+ # @example
21
+ # expect {
22
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
23
+ # }.to have_broadcasted_to("messages")
24
+ #
25
+ # expect {
26
+ # SomeChannel.broadcast_to(user)
27
+ # }.to have_broadcasted_to(user).from_channel(SomeChannel)
28
+ #
29
+ # # Using alias
30
+ # expect {
31
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
32
+ # }.to broadcast_to("messages")
33
+ #
34
+ # expect {
35
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
36
+ # ActionCable.server.broadcast "all", text: 'Hi!'
37
+ # }.to have_broadcasted_to("messages").exactly(:once)
38
+ #
39
+ # expect {
40
+ # 3.times { ActionCable.server.broadcast "messages", text: 'Hi!' }
41
+ # }.to have_broadcasted_to("messages").at_least(2).times
42
+ #
43
+ # expect {
44
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
45
+ # }.to have_broadcasted_to("messages").at_most(:twice)
46
+ #
47
+ # expect {
48
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
49
+ # }.to have_broadcasted_to("messages").with(text: 'Hi!')
50
+ def have_broadcasted_to(target = nil)
51
+ check_action_cable_adapter
52
+
53
+ ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
54
+ end
55
+
56
+ alias_matcher :broadcast_to, :have_broadcasted_to do |desc|
57
+ desc.gsub("have broadcasted", "broadcast")
58
+ end
59
+
60
+ private
61
+
62
+ # @private
63
+ def check_action_cable_adapter
64
+ return if ::ActionCable::SubscriptionAdapter::Test === ::ActionCable.server.pubsub
65
+
66
+ raise StandardError, "To use ActionCable matchers set `adapter: test` in your cable.yml"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,73 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # Namespace for various implementations of ActionMailbox features
5
+ #
6
+ # @api private
7
+ module ActionMailbox
8
+ # @private
9
+ class Base < RSpec::Rails::Matchers::BaseMatcher
10
+ private
11
+
12
+ def create_inbound_email(message)
13
+ RSpec::Rails::MailboxExampleGroup.create_inbound_email(message)
14
+ end
15
+ end
16
+
17
+ # @private
18
+ class ReceiveInboundEmail < Base
19
+ def initialize(message)
20
+ super()
21
+
22
+ @inbound_email = create_inbound_email(message)
23
+ end
24
+
25
+ if defined?(::ApplicationMailbox) && ::ApplicationMailbox.router.respond_to?(:mailbox_for)
26
+ def matches?(mailbox)
27
+ @mailbox = mailbox
28
+ @receiver = ApplicationMailbox.router.mailbox_for(inbound_email)
29
+
30
+ @receiver == @mailbox
31
+ end
32
+ else
33
+ def matches?(mailbox)
34
+ @mailbox = mailbox
35
+ @receiver = ApplicationMailbox.router.send(:match_to_mailbox, inbound_email)
36
+
37
+ @receiver == @mailbox
38
+ end
39
+ end
40
+
41
+ def failure_message
42
+ "expected #{describe_inbound_email} to route to #{mailbox}".tap do |msg|
43
+ if receiver
44
+ msg << ", but routed to #{receiver} instead"
45
+ end
46
+ end
47
+ end
48
+
49
+ def failure_message_when_negated
50
+ "expected #{describe_inbound_email} not to route to #{mailbox}"
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :inbound_email, :mailbox, :receiver
56
+
57
+ def describe_inbound_email
58
+ "mail to #{inbound_email.mail.to.to_sentence}"
59
+ end
60
+ end
61
+ end
62
+
63
+ # @api public
64
+ # Passes if the given inbound email would be routed to the subject inbox.
65
+ #
66
+ # @param message [Hash, Mail::Message] a mail message or hash of
67
+ # attributes used to build one
68
+ def receive_inbound_email(message)
69
+ ActionMailbox::ReceiveInboundEmail.new(message)
70
+ end
71
+ end
72
+ end
73
+ end