action-cable-testing 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee3c261b614627cd0d9d72bc9084cb4b7408bacaca953260d4c372da8774676c
4
- data.tar.gz: bbdde5b4fdb2deb81571c23198443674733962100311b8f013311f4a5a778307
3
+ metadata.gz: 6e9a6464f3ec1cc6ee48f51d97f0a8e0bebd34c3fee8f9523f01a6e0b6f19382
4
+ data.tar.gz: 3db543599b78e0944fa89679d47ab02275c09714b59b134430662a74163cddd8
5
5
  SHA512:
6
- metadata.gz: 4ebdc170789756ee2885f503241048270ecf815b35881474a972373cc51697d25f47d823ebc8fa8b8f0a55f2f2ffa30d31fac3e19b6939e001058f72ad523ea0
7
- data.tar.gz: 588e39d4f0c257b0786b966780be8b9ec8299ed87809a98da94fcf3e612c3d3abca5f5dfb861562abe60d9b5a3282cc0ce2439746c8da6f78b4cc8c64bd0d561
6
+ metadata.gz: 787512840b4dda952c365f770fcfb5858d6cbfb0ce261ca9fcbeb3384fd32a0102f490e4a1e1840a4fa1a3f68b9d4a24a4840322104a33af1a3164036b02bf5b
7
+ data.tar.gz: 2558049b61e39c42f57bd7a5a98f85522129a7d27001c5721e6525c56ba24136d9de913d579c02fd95205a8653eefed99006da3e722602b3fa7815eae69c9eda
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.4.0 (2019-01-10)
6
+
7
+ - Add stream assert methods and matchers. ([@sponomarev][])
8
+
9
+ See https://github.com/palkan/action-cable-testing/pull/42
10
+
5
11
  - Add session support for connection. ([@sponomarev][])
6
12
 
7
13
  See https://github.com/palkan/action-cable-testing/pull/35
data/README.md CHANGED
@@ -79,7 +79,10 @@ class ChatChannelTest < ActionCable::Channel::TestCase
79
79
  assert subscription.confirmed?
80
80
 
81
81
  # Asserts that the channel subscribes connection to a stream
82
- assert "chat_1", streams.last
82
+ assert_has_stream "chat_1"
83
+
84
+ # Asserts that the channel subscribes connection to a stream created with `stream_for`
85
+ assert_has_stream_for Room.find(1)
83
86
  end
84
87
 
85
88
  def test_does_not_subscribe_without_room_number
@@ -87,6 +90,9 @@ class ChatChannelTest < ActionCable::Channel::TestCase
87
90
 
88
91
  # Asserts that the subscription was rejected
89
92
  assert subscription.rejected?
93
+
94
+ # Asserts that no streams was started
95
+ assert_no_streams
90
96
  end
91
97
  end
92
98
  ```
@@ -252,13 +258,19 @@ RSpec.describe ChatChannel, type: :channel do
252
258
  it "rejects when no room id" do
253
259
  subscribe
254
260
  expect(subscription).to be_rejected
261
+ expect(subscription).not_to have_streams
255
262
  end
256
263
 
257
264
  it "subscribes to a stream when room id is provided" do
258
265
  subscribe(room_id: 42)
259
266
 
260
267
  expect(subscription).to be_confirmed
261
- expect(streams).to include("chat_42")
268
+
269
+ # check particular stream by name
270
+ expect(subscription).to have_stream_from("chat_42")
271
+
272
+ # or directly by model if you create streams with `stream_for`
273
+ expect(subscription).to have_stream_for(Room.find(42))
262
274
  end
263
275
  end
264
276
  ```
@@ -84,7 +84,18 @@ module ActionCable
84
84
  # assert subscription.confirmed?
85
85
  #
86
86
  # # Asserts that the channel subscribes connection to a stream
87
- # assert_equal "chat_1", streams.last
87
+ # assert_has_stream "chat_1"
88
+ #
89
+ # # Asserts that the channel subscribes connection to a specific
90
+ # # stream created for a model
91
+ # assert_has_stream_for Room.find(1)
92
+ # end
93
+ #
94
+ # def test_does_not_stream_with_incorrect_room_number
95
+ # subscribe room_number: -1
96
+ #
97
+ # # Asserts that not streams was started
98
+ # assert_no_streams
88
99
  # end
89
100
  #
90
101
  # def test_does_not_subscribe_without_room_number
@@ -115,8 +126,6 @@ module ActionCable
115
126
  # An instance of the current channel, created when you call `subscribe`.
116
127
  # <b>transmissions</b>::
117
128
  # A list of all messages that have been transmitted into the channel.
118
- # <b>streams</b>::
119
- # A list of all created streams subscriptions (as identifiers) for the subscription.
120
129
  #
121
130
  #
122
131
  # == Channel is automatically inferred
@@ -149,7 +158,13 @@ module ActionCable
149
158
  class_attribute :_channel_class
150
159
 
151
160
  attr_reader :subscription
152
- delegate :streams, to: :subscription
161
+
162
+ def streams
163
+ ActiveSupport::Deprecation.warn "Use appropriate `assert_has_stream`, `assert_has_stream_for`, `assert_no_streams` " +
164
+ "assertion methods for minitest or `have_stream`, `have_stream_for` and `have_stream_from` matchers " +
165
+ "for RSpec. Direct access to `streams` is deprecated and is going to be removed in version 1.0"
166
+ subscription.streams
167
+ end
153
168
 
154
169
  ActiveSupport.run_load_hooks(:action_cable_channel_test_case, self)
155
170
  end
@@ -227,10 +242,51 @@ module ActionCable
227
242
  connection.transmissions.map { |data| data["message"] }.compact
228
243
  end
229
244
 
245
+ # Asserts that no streams have been started.
246
+ #
247
+ # def test_assert_no_started_stream
248
+ # subscribe
249
+ # assert_no_streams
250
+ # end
251
+ #
252
+ def assert_no_streams
253
+ assert subscription.streams.empty?, "No streams started was expected, but #{subscription.streams.count} found"
254
+ end
255
+
256
+ # Asserts that the specified stream has been started.
257
+ #
258
+ # def test_assert_started_stream
259
+ # subscribe
260
+ # assert_has_stream 'messages'
261
+ # end
262
+ #
263
+ def assert_has_stream(stream)
264
+ assert subscription.streams.include?(stream), "Stream #{stream} has not been started"
265
+ end
266
+
267
+ # Asserts that the specified stream for a model has started.
268
+ #
269
+ # def test_assert_started_stream_for
270
+ # subscribe id: 42
271
+ # assert_has_stream_for User.find(42)
272
+ # end
273
+ #
274
+ def assert_has_stream_for(object)
275
+ assert_has_stream(broadcasting_for(object))
276
+ end
277
+
230
278
  private
231
279
  def check_subscribed!
232
280
  raise "Must be subscribed!" if subscription.nil? || subscription.rejected?
233
281
  end
282
+
283
+ def broadcasting_for(stream_or_object)
284
+ return stream_or_object if stream_or_object.is_a?(String)
285
+
286
+ self.class.channel_class.broadcasting_for(
287
+ [self.class.channel_class.channel_name, stream_or_object]
288
+ )
289
+ end
234
290
  end
235
291
 
236
292
  include Behavior
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActionCable
4
4
  module Testing
5
- VERSION = "0.3.4"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rspec/rails/matchers/action_cable/have_streams"
4
+
3
5
  module RSpec
4
6
  module Rails
5
7
  # @api public
@@ -39,6 +41,24 @@ if defined?(ActionCable)
39
41
  def have_rejected_connection
40
42
  raise_error(::ActionCable::Connection::Authorization::UnauthorizedError)
41
43
  end
44
+
45
+ def have_streams
46
+ check_subscribed!
47
+
48
+ RSpec::Rails::Matchers::ActionCable::HaveStream.new
49
+ end
50
+
51
+ def have_stream_from(stream)
52
+ check_subscribed!
53
+
54
+ RSpec::Rails::Matchers::ActionCable::HaveStream.new(stream)
55
+ end
56
+
57
+ def have_stream_for(object)
58
+ check_subscribed!
59
+
60
+ RSpec::Rails::Matchers::ActionCable::HaveStream.new(broadcasting_for(object))
61
+ end
42
62
  end
43
63
  end
44
64
  end
@@ -1,5 +1,7 @@
1
1
  # rubocop: disable Style/FrozenStringLiteralComment
2
2
 
3
+ require "rspec/rails/matchers/action_cable/have_broadcasted_to"
4
+
3
5
  module RSpec
4
6
  module Rails
5
7
  module Matchers
@@ -7,167 +9,6 @@ module RSpec
7
9
  #
8
10
  # @api private
9
11
  module ActionCable
10
- # rubocop: disable Metrics/ClassLength
11
- # @private
12
- class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
13
- def initialize(target, channel:)
14
- @target = target
15
- @channel = channel
16
- @block = Proc.new {}
17
- set_expected_number(:exactly, 1)
18
- end
19
-
20
- def with(data = nil)
21
- @data = data
22
- @data = @data.with_indifferent_access if @data.is_a?(Hash)
23
- @block = Proc.new if block_given?
24
- self
25
- end
26
-
27
- def exactly(count)
28
- set_expected_number(:exactly, count)
29
- self
30
- end
31
-
32
- def at_least(count)
33
- set_expected_number(:at_least, count)
34
- self
35
- end
36
-
37
- def at_most(count)
38
- set_expected_number(:at_most, count)
39
- self
40
- end
41
-
42
- def times
43
- self
44
- end
45
-
46
- def once
47
- exactly(:once)
48
- end
49
-
50
- def twice
51
- exactly(:twice)
52
- end
53
-
54
- def thrice
55
- exactly(:thrice)
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 ||= if @target.is_a?(String)
104
- @target
105
- else
106
- check_channel_presence
107
- @channel.broadcasting_for([@channel.channel_name, @target])
108
- end
109
- end
110
-
111
- def check(messages)
112
- @matching_msgs, @unmatching_msgs = messages.partition do |msg|
113
- decoded = ActiveSupport::JSON.decode(msg)
114
- decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
115
-
116
- if @data.nil? || @data === decoded
117
- @block.call(decoded)
118
- true
119
- else
120
- false
121
- end
122
- end
123
-
124
- @matching_msgs_count = @matching_msgs.size
125
-
126
- case @expectation_type
127
- when :exactly then @expected_number == @matching_msgs_count
128
- when :at_most then @expected_number >= @matching_msgs_count
129
- when :at_least then @expected_number <= @matching_msgs_count
130
- end
131
- end
132
-
133
- def set_expected_number(relativity, count)
134
- @expectation_type = relativity
135
- @expected_number =
136
- case count
137
- when :once then 1
138
- when :twice then 2
139
- when :thrice then 3
140
- else Integer(count)
141
- end
142
- end
143
-
144
- def base_message
145
- "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
146
- msg << " with #{data_description(@data)}" unless @data.nil?
147
- msg << ", but broadcast #{@matching_msgs_count}"
148
- end
149
- end
150
-
151
- def data_description(data)
152
- if data.is_a?(RSpec::Matchers::Composable)
153
- data.description
154
- else
155
- data
156
- end
157
- end
158
-
159
- def pubsub_adapter
160
- ::ActionCable.server.pubsub
161
- end
162
-
163
- def check_channel_presence
164
- return if @channel.present? && @channel.respond_to?(:channel_name)
165
-
166
- error_msg = "Broadcastnig channel can't be infered. Please, specify it with `from_channel`"
167
- raise ArgumentError, error_msg
168
- end
169
- end
170
- # rubocop: enable Metrics/ClassLength
171
12
  end
172
13
 
173
14
  # @api public
@@ -0,0 +1,170 @@
1
+ # rubocop: disable Style/FrozenStringLiteralComment
2
+
3
+ module RSpec
4
+ module Rails
5
+ module Matchers
6
+ module ActionCable
7
+ # rubocop: disable Metrics/ClassLength
8
+ # @private
9
+ class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
10
+ def initialize(target, channel:)
11
+ @target = target
12
+ @channel = channel
13
+ @block = Proc.new {}
14
+ set_expected_number(:exactly, 1)
15
+ end
16
+
17
+ def with(data = nil)
18
+ @data = data
19
+ @data = @data.with_indifferent_access if @data.is_a?(Hash)
20
+ @block = Proc.new if block_given?
21
+ self
22
+ end
23
+
24
+ def exactly(count)
25
+ set_expected_number(:exactly, count)
26
+ self
27
+ end
28
+
29
+ def at_least(count)
30
+ set_expected_number(:at_least, count)
31
+ self
32
+ end
33
+
34
+ def at_most(count)
35
+ set_expected_number(:at_most, count)
36
+ self
37
+ end
38
+
39
+ def times
40
+ self
41
+ end
42
+
43
+ def once
44
+ exactly(:once)
45
+ end
46
+
47
+ def twice
48
+ exactly(:twice)
49
+ end
50
+
51
+ def thrice
52
+ exactly(:thrice)
53
+ end
54
+
55
+ def failure_message
56
+ "expected to broadcast #{base_message}".tap do |msg|
57
+ if @unmatching_msgs.any?
58
+ msg << "\nBroadcasted messages to #{stream}:"
59
+ @unmatching_msgs.each do |data|
60
+ msg << "\n #{data}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def failure_message_when_negated
67
+ "expected not to broadcast #{base_message}"
68
+ end
69
+
70
+ def message_expectation_modifier
71
+ case @expectation_type
72
+ when :exactly then "exactly"
73
+ when :at_most then "at most"
74
+ when :at_least then "at least"
75
+ end
76
+ end
77
+
78
+ def supports_block_expectations?
79
+ true
80
+ end
81
+
82
+ def matches?(proc)
83
+ raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
84
+
85
+ original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
86
+ proc.call
87
+ in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)
88
+
89
+ check(in_block_messages)
90
+ end
91
+
92
+ def from_channel(channel)
93
+ @channel = channel
94
+ self
95
+ end
96
+
97
+ private
98
+
99
+ def stream
100
+ @stream ||= if @target.is_a?(String)
101
+ @target
102
+ else
103
+ check_channel_presence
104
+ @channel.broadcasting_for([@channel.channel_name, @target])
105
+ end
106
+ end
107
+
108
+ def check(messages)
109
+ @matching_msgs, @unmatching_msgs = messages.partition do |msg|
110
+ decoded = ActiveSupport::JSON.decode(msg)
111
+ decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
112
+
113
+ if @data.nil? || @data === decoded
114
+ @block.call(decoded)
115
+ true
116
+ else
117
+ false
118
+ end
119
+ end
120
+
121
+ @matching_msgs_count = @matching_msgs.size
122
+
123
+ case @expectation_type
124
+ when :exactly then @expected_number == @matching_msgs_count
125
+ when :at_most then @expected_number >= @matching_msgs_count
126
+ when :at_least then @expected_number <= @matching_msgs_count
127
+ end
128
+ end
129
+
130
+ def set_expected_number(relativity, count)
131
+ @expectation_type = relativity
132
+ @expected_number =
133
+ case count
134
+ when :once then 1
135
+ when :twice then 2
136
+ when :thrice then 3
137
+ else Integer(count)
138
+ end
139
+ end
140
+
141
+ def base_message
142
+ "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
143
+ msg << " with #{data_description(@data)}" unless @data.nil?
144
+ msg << ", but broadcast #{@matching_msgs_count}"
145
+ end
146
+ end
147
+
148
+ def data_description(data)
149
+ if data.is_a?(RSpec::Matchers::Composable)
150
+ data.description
151
+ else
152
+ data
153
+ end
154
+ end
155
+
156
+ def pubsub_adapter
157
+ ::ActionCable.server.pubsub
158
+ end
159
+
160
+ def check_channel_presence
161
+ return if @channel.present? && @channel.respond_to?(:channel_name)
162
+
163
+ error_msg = "Broadcastnig channel can't be infered. Please, specify it with `from_channel`"
164
+ raise ArgumentError, error_msg
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,60 @@
1
+ # rubocop: disable Style/FrozenStringLiteralComment
2
+
3
+ module RSpec
4
+ module Rails
5
+ module Matchers
6
+ module ActionCable
7
+ # @api private
8
+ # Provides the implementation for `have_stream`, `have_stream_for`, and `have_stream_from`.
9
+ # Not intended to be instantiated directly.
10
+ class HaveStream < RSpec::Matchers::BuiltIn::BaseMatcher
11
+ # @api private
12
+ # @return [String]
13
+ def failure_message
14
+ "expected to have #{base_message}"
15
+ end
16
+
17
+ # @api private
18
+ # @return [String]
19
+ def failure_message_when_negated
20
+ "expected not to have #{base_message}"
21
+ end
22
+
23
+ # @api private
24
+ # @return [Boolean]
25
+ def matches?(subscription)
26
+ raise(ArgumentError, "have_streams is used for negated expectations only") if no_expected?
27
+
28
+ match(subscription)
29
+ end
30
+
31
+ # @api private
32
+ # @return [Boolean]
33
+ def does_not_match?(subscription)
34
+ !match(subscription)
35
+ end
36
+
37
+ private
38
+
39
+ def match(subscription)
40
+ case subscription
41
+ when ::ActionCable::Channel::Base
42
+ @actual = subscription.streams
43
+ no_expected? ? actual.any? : actual.any? { |i| expected === i }
44
+ else
45
+ raise ArgumentError, "have_stream, have_stream_from and have_stream_from support expectations on subscription only"
46
+ end
47
+ end
48
+
49
+ def base_message
50
+ no_expected? ? "any stream started" : "stream #{expected_formatted} started, but have #{actual_formatted}"
51
+ end
52
+
53
+ def no_expected?
54
+ !defined?(@expected)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action-cable-testing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-06 00:00:00.000000000 Z
11
+ date: 2019-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -28,14 +28,14 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.10'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.10'
41
41
  - !ruby/object:Gem::Dependency
@@ -162,6 +162,8 @@ files:
162
162
  - lib/generators/test_unit/channel/templates/unit_test.rb.erb
163
163
  - lib/rspec/rails/example/channel_example_group.rb
164
164
  - lib/rspec/rails/matchers/action_cable.rb
165
+ - lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb
166
+ - lib/rspec/rails/matchers/action_cable/have_streams.rb
165
167
  - lib/rspec/rails/shared_contexts/action_cable.rb
166
168
  homepage: http://github.com/palkan/action-cable-testing
167
169
  licenses:
@@ -183,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
185
  version: '0'
184
186
  requirements: []
185
187
  rubyforge_project:
186
- rubygems_version: 2.7.7
188
+ rubygems_version: 2.7.6
187
189
  signing_key:
188
190
  specification_version: 4
189
191
  summary: Testing utils for Action Cable