action-cable-testing 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/README.md +48 -1
- data/lib/action_cable/test_helper.rb +30 -15
- data/lib/action_cable/testing/version.rb +1 -1
- data/lib/rspec/rails/matchers/action_cable.rb +41 -10
- data/lib/rspec/rails/shared_contexts/action_cable.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49ea4d058ad808b422aee252af3404f7ffc19b09
|
4
|
+
data.tar.gz: fc2a1ab66b7d9dbbcb34a8c59d51f614fe715e8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6457b2c9afabf9c6e758a5cbe1cad993c7585734dec33bfed1a96e4d606dcc415e507cefc3d0e6fc7acbcca877edd4d558798919ef2f700846c40d7185faecd7
|
7
|
+
data.tar.gz: 9cc75eb56ad1f4b6f30540e5d3b523be3ab6278604bbc97325e1b3d1d84024dbd72ea9e7feb0894732b134c4832a4040c2c76843d6d14d4e690ae244c91d2b81
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.2.0
|
4
4
|
|
5
|
-
-
|
5
|
+
- Update minitest's `assert_broadcast_on` and `assert_broadcasts` matchers to support a record as an argument. ([@thesmartnik][])
|
6
|
+
|
7
|
+
See https://github.com/palkan/action-cable-testing/issues/11
|
8
|
+
|
9
|
+
- Update `have_broadcasted_to` matcher to support a record as an argument. ([@thesmartnik][])
|
10
|
+
|
11
|
+
See https://github.com/palkan/action-cable-testing/issues/9
|
12
|
+
|
13
|
+
## 0.1.2 (2017-11-14)
|
14
|
+
|
15
|
+
- Add RSpec shared contexts to switch between adapters. ([@palkan][])
|
6
16
|
|
7
17
|
See https://github.com/palkan/action-cable-testing/issues/4.
|
8
18
|
|
@@ -15,3 +25,4 @@ See https://github.com/palkan/action-cable-testing/issues/4.
|
|
15
25
|
- Initial version. ([@palkan][])
|
16
26
|
|
17
27
|
[@palkan]: https://github.com/palkan
|
28
|
+
[@thesmartnik]: https://github.com/thesmartnik
|
data/README.md
CHANGED
@@ -52,10 +52,16 @@ class ExampleTest < ActionDispatch::IntegrationTest
|
|
52
52
|
|
53
53
|
def test_broadcasts
|
54
54
|
room = rooms(:office)
|
55
|
-
|
55
|
+
|
56
56
|
assert_broadcast_on("messages:#{room.id}", text: 'Hello!') do
|
57
57
|
post "/say/#{room.id}", xhr: true, params: { message: 'Hello!' }
|
58
58
|
end
|
59
|
+
|
60
|
+
# When testing broadcast to an object outside of channel test,
|
61
|
+
# channel should be explicitly specified
|
62
|
+
assert_broadcast_on(room, { text: 'Hello!' }, { channel: ChatChannel } do
|
63
|
+
post "/say/#{room.id}", xhr: true, params: { message: 'Hello!' }
|
64
|
+
end
|
59
65
|
end
|
60
66
|
end
|
61
67
|
```
|
@@ -121,6 +127,34 @@ class ChatChannelTest < ActionCable::Channel::TestCase
|
|
121
127
|
end
|
122
128
|
end
|
123
129
|
```
|
130
|
+
When broadcasting to an object:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class ChatChannelTest < ActionCable::Channel::TestCase
|
134
|
+
include ActionCable::TestHelper
|
135
|
+
|
136
|
+
def setup
|
137
|
+
@room = Room.find 1
|
138
|
+
|
139
|
+
stub_connection(user: users[:john])
|
140
|
+
subscribe room_number: room.id
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_broadcating
|
144
|
+
assert_broadcasts(@room, 1) do
|
145
|
+
perform :speak, message: "I'm here!"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# or
|
150
|
+
|
151
|
+
def test_broadcasted_data
|
152
|
+
assert_broadcasts_on(@room, text: "I'm here!", from: "John") do
|
153
|
+
perform :speak, message: "I'm here!"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
124
158
|
|
125
159
|
### RSpec Usage
|
126
160
|
|
@@ -152,6 +186,19 @@ RSpec.describe CommentsController do
|
|
152
186
|
end
|
153
187
|
```
|
154
188
|
|
189
|
+
Or when broacasting to an object:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
RSpec.describe CommentsController do
|
193
|
+
describe "POST #create" do
|
194
|
+
let(:post) { create :post }
|
195
|
+
|
196
|
+
expect { post :create, comment: { text: 'Cool!', post_id: post.id } }.to
|
197
|
+
have_broadcasted_to(post).from_channel(PostChannel).with(text: 'Cool!')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
155
202
|
You can also unit-test your channels:
|
156
203
|
|
157
204
|
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module ActionCable
|
4
4
|
# Provides helper methods for testing Action Cable broadcasting
|
5
5
|
module TestHelper
|
6
|
+
CHANNEL_NOT_FOUND = ArgumentError.new("Broadcastnig channel can't be infered. Please, specify it with `:channel`")
|
7
|
+
|
6
8
|
def before_setup # :nodoc:
|
7
9
|
server = ActionCable.server
|
8
10
|
test_adapter = ActionCable::SubscriptionAdapter::Test.new(server)
|
@@ -42,15 +44,17 @@ module ActionCable
|
|
42
44
|
# end
|
43
45
|
# end
|
44
46
|
#
|
45
|
-
def assert_broadcasts(
|
47
|
+
def assert_broadcasts(target, number, channel: nil)
|
48
|
+
stream = stream(target, channel)
|
49
|
+
|
46
50
|
if block_given?
|
47
|
-
original_count = broadcasts_size(
|
51
|
+
original_count = broadcasts_size(stream)
|
48
52
|
yield
|
49
|
-
new_count = broadcasts_size(
|
50
|
-
assert_equal number, new_count - original_count, "#{number} broadcasts to #{
|
53
|
+
new_count = broadcasts_size(stream)
|
54
|
+
assert_equal number, new_count - original_count, "#{number} broadcasts to #{stream} expected, but #{new_count - original_count} were sent"
|
51
55
|
else
|
52
|
-
actual_count = broadcasts_size(
|
53
|
-
assert_equal number, actual_count, "#{number} broadcasts to #{
|
56
|
+
actual_count = broadcasts_size(stream)
|
57
|
+
assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
@@ -74,8 +78,8 @@ module ActionCable
|
|
74
78
|
#
|
75
79
|
# assert_broadcasts 'messages', 0, &block
|
76
80
|
#
|
77
|
-
def assert_no_broadcasts(
|
78
|
-
assert_broadcasts
|
81
|
+
def assert_no_broadcasts(target, &block)
|
82
|
+
assert_broadcasts target, 0, &block
|
79
83
|
end
|
80
84
|
|
81
85
|
# Asserts that the specified message has been sent to the channel.
|
@@ -93,28 +97,30 @@ module ActionCable
|
|
93
97
|
# end
|
94
98
|
# end
|
95
99
|
#
|
96
|
-
def assert_broadcast_on(
|
100
|
+
def assert_broadcast_on(target, data, channel: nil)
|
97
101
|
# Encode to JSON and back–we want to use this value to compare
|
98
102
|
# with decoded JSON.
|
99
103
|
# Comparing JSON strings doesn't work due to the order if the keys.
|
100
104
|
serialized_msg =
|
101
105
|
ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data))
|
102
|
-
|
106
|
+
stream = stream(target, channel)
|
107
|
+
|
108
|
+
new_messages = broadcasts(stream)
|
103
109
|
if block_given?
|
104
110
|
old_messages = new_messages
|
105
|
-
clear_messages(
|
111
|
+
clear_messages(stream)
|
106
112
|
|
107
113
|
yield
|
108
|
-
new_messages = broadcasts(
|
109
|
-
clear_messages(
|
114
|
+
new_messages = broadcasts(stream)
|
115
|
+
clear_messages(stream)
|
110
116
|
|
111
117
|
# Restore all sent messages
|
112
|
-
(old_messages + new_messages).each { |m| pubsub_adapter.broadcast(
|
118
|
+
(old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
|
113
119
|
end
|
114
120
|
|
115
121
|
message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }
|
116
122
|
|
117
|
-
assert message, "No messages sent with #{data} to #{
|
123
|
+
assert message, "No messages sent with #{data} to #{stream}"
|
118
124
|
end
|
119
125
|
|
120
126
|
def pubsub_adapter # :nodoc:
|
@@ -127,5 +133,14 @@ module ActionCable
|
|
127
133
|
def broadcasts_size(channel) # :nodoc:
|
128
134
|
broadcasts(channel).size
|
129
135
|
end
|
136
|
+
|
137
|
+
def stream(target, channel = nil)
|
138
|
+
return target if target.is_a?(String)
|
139
|
+
|
140
|
+
channel ||= @subscription
|
141
|
+
raise CHANNEL_NOT_FOUND unless channel && channel.respond_to?(:channel_name)
|
142
|
+
|
143
|
+
channel.broadcasting_for([channel.channel_name, target])
|
144
|
+
end
|
130
145
|
end
|
131
146
|
end
|
@@ -10,8 +10,9 @@ module RSpec
|
|
10
10
|
# rubocop: disable Style/ClassLength
|
11
11
|
# @private
|
12
12
|
class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(target, channel:)
|
14
|
+
@target = target
|
15
|
+
@channel = channel
|
15
16
|
@block = Proc.new {}
|
16
17
|
set_expected_number(:exactly, 1)
|
17
18
|
end
|
@@ -57,7 +58,7 @@ module RSpec
|
|
57
58
|
def failure_message
|
58
59
|
"expected to broadcast #{base_message}".tap do |msg|
|
59
60
|
if @unmatching_msgs.any?
|
60
|
-
msg << "\nBroadcasted messages to #{
|
61
|
+
msg << "\nBroadcasted messages to #{stream}:"
|
61
62
|
@unmatching_msgs.each do |data|
|
62
63
|
msg << "\n #{data}"
|
63
64
|
end
|
@@ -84,15 +85,29 @@ module RSpec
|
|
84
85
|
def matches?(proc)
|
85
86
|
raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
|
86
87
|
|
87
|
-
original_sent_messages_count = pubsub_adapter.broadcasts(
|
88
|
+
original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
|
88
89
|
proc.call
|
89
|
-
in_block_messages = pubsub_adapter.broadcasts(
|
90
|
+
in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)
|
90
91
|
|
91
92
|
check(in_block_messages)
|
92
93
|
end
|
93
94
|
|
95
|
+
def from_channel(channel)
|
96
|
+
@channel = channel
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
94
100
|
private
|
95
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
|
+
|
96
111
|
def check(messages)
|
97
112
|
@matching_msgs, @unmatching_msgs = messages.partition do |msg|
|
98
113
|
decoded = ActiveSupport::JSON.decode(msg)
|
@@ -127,7 +142,7 @@ module RSpec
|
|
127
142
|
end
|
128
143
|
|
129
144
|
def base_message
|
130
|
-
"#{message_expectation_modifier} #{@expected_number} messages to #{
|
145
|
+
"#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
|
131
146
|
msg << " with #{data_description(@data)}" unless @data.nil?
|
132
147
|
msg << ", but broadcast #{@matching_msgs_count}"
|
133
148
|
end
|
@@ -144,18 +159,32 @@ module RSpec
|
|
144
159
|
def pubsub_adapter
|
145
160
|
::ActionCable.server.pubsub
|
146
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
|
147
169
|
end
|
148
170
|
# rubocop: enable Style/ClassLength
|
149
171
|
end
|
150
172
|
|
151
173
|
# @api public
|
152
|
-
# Passes if a message has been sent to a stream inside
|
174
|
+
# Passes if a message has been sent to a stream/object inside a block.
|
175
|
+
# May chain `at_least`, `at_most` or `exactly` to specify a number of times.
|
176
|
+
# To specify channel from which message has been broadcasted to object use `from_channel`.
|
177
|
+
#
|
153
178
|
#
|
154
179
|
# @example
|
155
180
|
# expect {
|
156
181
|
# ActionCable.server.broadcast "messages", text: 'Hi!'
|
157
182
|
# }.to have_broadcasted_to("messages")
|
158
183
|
#
|
184
|
+
# expect {
|
185
|
+
# SomeChannel.broadcast_to(user)
|
186
|
+
# }.to have_broadcasted_to(user).from_channel(SomeChannel)
|
187
|
+
#
|
159
188
|
# # Using alias
|
160
189
|
# expect {
|
161
190
|
# ActionCable.server.broadcast "messages", text: 'Hi!'
|
@@ -177,9 +206,11 @@ module RSpec
|
|
177
206
|
# expect {
|
178
207
|
# ActionCable.server.broadcast "messages", text: 'Hi!'
|
179
208
|
# }.to have_broadcasted_to("messages").with(text: 'Hi!')
|
180
|
-
|
209
|
+
|
210
|
+
def have_broadcasted_to(target = nil)
|
181
211
|
check_action_cable_adapter
|
182
|
-
|
212
|
+
|
213
|
+
ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
|
183
214
|
end
|
184
215
|
alias_method :broadcast_to, :have_broadcasted_to
|
185
216
|
|
@@ -188,7 +219,7 @@ module RSpec
|
|
188
219
|
# @private
|
189
220
|
def check_action_cable_adapter
|
190
221
|
return if ::ActionCable::SubscriptionAdapter::Test === ::ActionCable.server.pubsub
|
191
|
-
raise StandardError, "To use ActionCable matchers set `adapter:
|
222
|
+
raise StandardError, "To use ActionCable matchers set `adapter: test` in your cable.yml"
|
192
223
|
end
|
193
224
|
end
|
194
225
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Generate contexts to use specific Action Cable adapter:
|
4
4
|
# - "action_cable:async" (action_cable: :async)
|
5
5
|
# - "action_cable:inline" (action_cable: :inline)
|
6
|
-
# - "action_cable:test" (
|
6
|
+
# - "action_cable:test" (action_cable: :test)
|
7
7
|
%w[async inline test].each do |adapter|
|
8
8
|
RSpec.shared_context "action_cable:#{adapter}" do
|
9
9
|
require "action_cable/subscription_adapter/#{adapter}"
|
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.
|
4
|
+
version: 0.2.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: 2017-
|
11
|
+
date: 2017-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actioncable
|