action-cable-testing 0.0.6.1 → 0.0.7

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
  SHA1:
3
- metadata.gz: 8092ab8d50462d20a5192f06dd233f4876a130e9
4
- data.tar.gz: 37059d739700e6792a972e405c8a2749f65cc2c2
3
+ metadata.gz: 96625e06dd39e7a58e78f77601bdc84d440cad8a
4
+ data.tar.gz: e125b688d9c10e7ad1ed89efeb25bc8c2e7f225b
5
5
  SHA512:
6
- metadata.gz: 11c87c8eda12f74a453985af75907535bafb90f65e63a9ca588cd2e91ab820d769f85fc2aeea8c4ab8d0bdffc5c1502a0c65c60cde07409de272937bcf1983e6
7
- data.tar.gz: 3191e647f6a54aa412c98a9f7585ca45f411d786566c6fb5e1fe54c817aeb2b054907e770d385754a612a7824048c86fe953b530600bdfd709251e49896eb472
6
+ metadata.gz: 17592526b34d09527d340b414dd0d9664a11fab820fd5d8aa49f5f67ea55c5494cb0044f83d6555d23d6ed745603923e808015b544cc797c6018dc0eb5289f57
7
+ data.tar.gz: e9c91223b1702d725293ff5e7303e2c2d6bc39a8b90f550ff1d37a98bedd19367c55dd414d8b2a9eced77e922de03e74d792ec441dd9f418b322682f3ff0085a
data/README.md CHANGED
@@ -124,6 +124,54 @@ end
124
124
 
125
125
  ### RSpec Usage
126
126
 
127
+
128
+ First, set your adapter to `test` in `cable.yml`:
129
+
130
+ ```yml
131
+ # config/cable.yml
132
+ test:
133
+ adapter: test
134
+ ```
135
+
136
+ Now you can use `have_broadcasted_to` / `broadcast_to` matchers anywhere in your specs. For example:
137
+
138
+ ```ruby
139
+ RSpec.describe CommentsController do
140
+ describe "POST #create" do
141
+ expect { post :create, comment: { text: 'Cool!' } }.to
142
+ have_broadcasted_to("comments").with(text: 'Cool!')
143
+ end
144
+ end
145
+ ```
146
+
147
+ You can also unit-test your channels:
148
+
149
+
150
+ ```ruby
151
+ # spec/channels/chat_channel_spec.rb
152
+
153
+ require "rails_helper"
154
+
155
+ RSpec.describe ChatChannel, type: :channel do
156
+ before do
157
+ # initialize connection with identifiers
158
+ stub_connection user_id: user.id
159
+ end
160
+
161
+ it "rejects when no room id" do
162
+ subscribe
163
+ expect(subscription).to be_rejected
164
+ end
165
+
166
+ it "subscribes to a stream when room id is provided" do
167
+ subscribe(room_id: 42)
168
+
169
+ expect(subscription).to be_confirmed
170
+ expect(streams).to include("chat_42")
171
+ end
172
+ end
173
+ ```
174
+
127
175
  For more RSpec documentation see https://relishapp.com/palkan/action-cable-testing/docs.
128
176
 
129
177
  ### Generators
@@ -97,7 +97,7 @@ module ActionCable
97
97
  #
98
98
  # perform :speak, message: "Hello, Rails!"
99
99
  #
100
- # assert_equal "Hello, Rails!", transmissions.last["message"]["text"]
100
+ # assert_equal "Hello, Rails!", transmissions.last["text"]
101
101
  # end
102
102
  #
103
103
  # == Special methods
@@ -110,7 +110,7 @@ module ActionCable
110
110
  # <b>subscription</b>::
111
111
  # An instance of the current channel, created when you call `subscribe`.
112
112
  # <b>transmissions</b>::
113
- # A list of all messages that have been transmitted into the connection (without encoding).
113
+ # A list of all messages that have been transmitted into the channel.
114
114
  # <b>streams</b>::
115
115
  # A list of all created streams subscriptions (as identifiers) for the subscription.
116
116
  #
@@ -21,7 +21,7 @@ module ActionCable
21
21
  channels_data[channel] = []
22
22
  end
23
23
 
24
- def reset!
24
+ def clear
25
25
  @channels_data = nil
26
26
  end
27
27
 
@@ -112,9 +112,9 @@ module ActionCable
112
112
  (old_messages + new_messages).each { |m| pubsub_adapter.broadcast(channel, m) }
113
113
  end
114
114
 
115
- new_messages = new_messages.map { |msg| ActiveSupport::JSON.decode(msg) }
115
+ message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }
116
116
 
117
- assert_includes new_messages, serialized_msg, "No messages sent with #{data} to #{channel}"
117
+ assert message, "No messages sent with #{data} to #{channel}"
118
118
  end
119
119
 
120
120
  def pubsub_adapter # :nodoc:
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rspec/rails/example/channel_example_group"
4
+ require "rspec/rails/matchers/action_cable"
4
5
 
5
6
  module RSpec # :nodoc:
6
7
  module Rails
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActionCable
4
4
  module Testing
5
- VERSION = "0.0.6.1"
5
+ VERSION = "0.0.7"
6
6
  end
7
7
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/rspec"
4
+
5
+ module Rspec
6
+ module Generators
7
+ # @private
8
+ class ChannelGenerator < Base
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ def create_channel_spec
12
+ template "channel_spec.rb.erb", File.join("spec/channels", class_path, "#{file_name}_channel_spec.rb")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'rails_helper'
2
+
3
+ <% module_namespacing do -%>
4
+ RSpec.describe <%= class_name %>Channel, <%= type_metatag(:channel) %> do
5
+ pending "add some examples to (or delete) #{__FILE__}"
6
+ end
7
+ <% end -%>
@@ -0,0 +1,195 @@
1
+ # rubocop: disable Style/FrozenStringLiteralComment
2
+
3
+ module RSpec
4
+ module Rails
5
+ module Matchers
6
+ # Namespace for various implementations of ActionCable features
7
+ #
8
+ # @api private
9
+ module ActionCable
10
+ # rubocop: disable Style/ClassLength
11
+ # @private
12
+ class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
13
+ def initialize(stream)
14
+ @stream = stream
15
+ @block = Proc.new {}
16
+ set_expected_number(:exactly, 1)
17
+ end
18
+
19
+ def with(data = nil)
20
+ @data = data
21
+ @data = @data.with_indifferent_access if @data.is_a?(Hash)
22
+ @block = Proc.new if block_given?
23
+ self
24
+ end
25
+
26
+ def exactly(count)
27
+ set_expected_number(:exactly, count)
28
+ self
29
+ end
30
+
31
+ def at_least(count)
32
+ set_expected_number(:at_least, count)
33
+ self
34
+ end
35
+
36
+ def at_most(count)
37
+ set_expected_number(:at_most, count)
38
+ self
39
+ end
40
+
41
+ def times
42
+ self
43
+ end
44
+
45
+ def once
46
+ exactly(:once)
47
+ end
48
+
49
+ def twice
50
+ exactly(:twice)
51
+ end
52
+
53
+ def thrice
54
+ exactly(:thrice)
55
+ end
56
+
57
+ def failure_message
58
+ "expected to broadcast #{base_message}".tap do |msg|
59
+ if @unmatching_msgs.any?
60
+ msg << "\nBroadcasted messages to #{@stream}:"
61
+ @unmatching_msgs.each do |data|
62
+ msg << "\n #{data}"
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def failure_message_when_negated
69
+ "expected not to broadcast #{base_message}"
70
+ end
71
+
72
+ def message_expectation_modifier
73
+ case @expectation_type
74
+ when :exactly then "exactly"
75
+ when :at_most then "at most"
76
+ when :at_least then "at least"
77
+ end
78
+ end
79
+
80
+ def supports_block_expectations?
81
+ true
82
+ end
83
+
84
+ def matches?(proc)
85
+ raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
86
+
87
+ original_sent_messages_count = pubsub_adapter.broadcasts(@stream).size
88
+ proc.call
89
+ in_block_messages = pubsub_adapter.broadcasts(@stream).drop(original_sent_messages_count)
90
+
91
+ check(in_block_messages)
92
+ end
93
+
94
+ private
95
+
96
+ def check(messages)
97
+ @matching_msgs, @unmatching_msgs = messages.partition do |msg|
98
+ decoded = ActiveSupport::JSON.decode(msg)
99
+ decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
100
+
101
+ if @data.nil? || @data === decoded
102
+ @block.call(decoded)
103
+ true
104
+ else
105
+ false
106
+ end
107
+ end
108
+
109
+ @matching_msgs_count = @matching_msgs.size
110
+
111
+ case @expectation_type
112
+ when :exactly then @expected_number == @matching_msgs_count
113
+ when :at_most then @expected_number >= @matching_msgs_count
114
+ when :at_least then @expected_number <= @matching_msgs_count
115
+ end
116
+ end
117
+
118
+ def set_expected_number(relativity, count)
119
+ @expectation_type = relativity
120
+ @expected_number =
121
+ case count
122
+ when :once then 1
123
+ when :twice then 2
124
+ when :thrice then 3
125
+ else Integer(count)
126
+ end
127
+ end
128
+
129
+ def base_message
130
+ "#{message_expectation_modifier} #{@expected_number} messages to #{@stream}".tap do |msg|
131
+ msg << " with #{data_description(@data)}" unless @data.nil?
132
+ msg << ", but broadcast #{@matching_msgs_count}"
133
+ end
134
+ end
135
+
136
+ def data_description(data)
137
+ if data.is_a?(RSpec::Matchers::Composable)
138
+ data.description
139
+ else
140
+ data
141
+ end
142
+ end
143
+
144
+ def pubsub_adapter
145
+ ::ActionCable.server.pubsub
146
+ end
147
+ end
148
+ # rubocop: enable Style/ClassLength
149
+ end
150
+
151
+ # @api public
152
+ # Passes if a message has been sent to a stream inside block. May chain at_least, at_most or exactly to specify a number of times.
153
+ #
154
+ # @example
155
+ # expect {
156
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
157
+ # }.to have_broadcasted_to("messages")
158
+ #
159
+ # # Using alias
160
+ # expect {
161
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
162
+ # }.to broadcast_to("messages")
163
+ #
164
+ # expect {
165
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
166
+ # ActionCable.server.broadcast "all", text: 'Hi!'
167
+ # }.to have_broadcasted_to("messages").exactly(:once)
168
+ #
169
+ # expect {
170
+ # 3.times { ActionCable.server.broadcast "messages", text: 'Hi!' }
171
+ # }.to have_broadcasted_to("messages").at_least(2).times
172
+ #
173
+ # expect {
174
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
175
+ # }.to have_broadcasted_to("messages").at_most(:twice)
176
+ #
177
+ # expect {
178
+ # ActionCable.server.broadcast "messages", text: 'Hi!'
179
+ # }.to have_broadcasted_to("messages").with(text: 'Hi!')
180
+ def have_broadcasted_to(stream = nil)
181
+ check_action_cable_adapter
182
+ ActionCable::HaveBroadcastedTo.new(stream)
183
+ end
184
+ alias_method :broadcast_to, :have_broadcasted_to
185
+
186
+ private
187
+
188
+ # @private
189
+ def check_action_cable_adapter
190
+ return if ::ActionCable::SubscriptionAdapter::Test === ::ActionCable.server.pubsub
191
+ raise StandardError, "To use ActionCable matchers set `adapter: :test` in your cable.yml"
192
+ end
193
+ end
194
+ end
195
+ 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.0.6.1
4
+ version: 0.0.7
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-10-23 00:00:00.000000000 Z
11
+ date: 2017-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -154,9 +154,12 @@ files:
154
154
  - lib/action_cable/testing.rb
155
155
  - lib/action_cable/testing/rspec.rb
156
156
  - lib/action_cable/testing/version.rb
157
+ - lib/generators/rspec/channel/channel_generator.rb
158
+ - lib/generators/rspec/channel/templates/channel_spec.rb.erb
157
159
  - lib/generators/test_unit/channel/channel_generator.rb
158
160
  - lib/generators/test_unit/channel/templates/unit_test.rb.erb
159
161
  - lib/rspec/rails/example/channel_example_group.rb
162
+ - lib/rspec/rails/matchers/action_cable.rb
160
163
  homepage: http://github.com/palkan/action-cable-testing
161
164
  licenses:
162
165
  - MIT