Skip to content

Commit

Permalink
Smart broadcast_to matcher (palkan#10)
Browse files Browse the repository at this point in the history
Accept objects as arguments for `broadcast_to` RSpec matcher.
Add optional `from_channel` modifier.
  • Loading branch information
TheSmartnik authored and palkan committed Nov 27, 2017
1 parent 684e0c1 commit 558af7a
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 9 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change log

## (2017-11-27) ([@thesmartnik][])

- Update `have_broadcasted_to` matcher to support a record as an argument.

See https://github.com/palkan/action-cable-testing/issues/9

## 0.1.2 (2017-11-14) ([@palkan][])

- Add RSpec shared contexts to switch between adapters.
Expand All @@ -15,3 +21,4 @@ See https://github.com/palkan/action-cable-testing/issues/4.
- Initial version. ([@palkan][])

[@palkan]: https://github.com/palkan
[@thesmartnik]: https://github.com/thesmartnik
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ RSpec.describe CommentsController do
end
```

Or when broacasting to an object:

```ruby
RSpec.describe CommentsController do
describe "POST #create" do
let(:post) { create :post }

expect { post :create, comment: { text: 'Cool!', post_id: post.id } }.to
have_broadcasted_to(post).from_channel(PostChannel).with(text: 'Cool!')
end
end
```

You can also unit-test your channels:


Expand Down
26 changes: 26 additions & 0 deletions features/matchers/have_broadcasted_matcher.feature
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,29 @@ Feature: have_broadcasted matcher
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the examples should all pass

Scenario: Checking broadcast to a record
Given a file named "spec/channels/chat_channel_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ChatChannel, :type => :channel do
it "successfully subscribes" do
user = User.new(42)
expect {
ChatChannel.broadcast_to(user, text: 'Hi')
}.to have_broadcasted_to(user)
end
end
"""
And a file named "app/models/user.rb" with:
"""ruby
class User < Struct.new(:name)
def to_global_id
name
end
end
"""
When I run `rspec spec/channels/chat_channel_spec.rb`
Then the example should pass
49 changes: 40 additions & 9 deletions lib/rspec/rails/matchers/action_cable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ module ActionCable
# rubocop: disable Style/ClassLength
# @private
class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
def initialize(stream)
@stream = stream
def initialize(target, channel:)
@target = target
@channel = channel
@block = Proc.new {}
set_expected_number(:exactly, 1)
end
Expand Down Expand Up @@ -57,7 +58,7 @@ def thrice
def failure_message
"expected to broadcast #{base_message}".tap do |msg|
if @unmatching_msgs.any?
msg << "\nBroadcasted messages to #{@stream}:"
msg << "\nBroadcasted messages to #{stream}:"
@unmatching_msgs.each do |data|
msg << "\n #{data}"
end
Expand All @@ -84,15 +85,29 @@ def supports_block_expectations?
def matches?(proc)
raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc

original_sent_messages_count = pubsub_adapter.broadcasts(@stream).size
original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
proc.call
in_block_messages = pubsub_adapter.broadcasts(@stream).drop(original_sent_messages_count)
in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)

check(in_block_messages)
end

def from_channel(channel)
@channel = channel
self
end

private

def stream
@stream ||= if @target.is_a?(String)
@target
else
check_channel_presence
@channel.broadcasting_for([@channel.channel_name, @target])
end
end

def check(messages)
@matching_msgs, @unmatching_msgs = messages.partition do |msg|
decoded = ActiveSupport::JSON.decode(msg)
Expand Down Expand Up @@ -127,7 +142,7 @@ def set_expected_number(relativity, count)
end

def base_message
"#{message_expectation_modifier} #{@expected_number} messages to #{@stream}".tap do |msg|
"#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
msg << " with #{data_description(@data)}" unless @data.nil?
msg << ", but broadcast #{@matching_msgs_count}"
end
Expand All @@ -144,18 +159,32 @@ def data_description(data)
def pubsub_adapter
::ActionCable.server.pubsub
end

def check_channel_presence
return if @channel.present? && @channel.respond_to?(:channel_name)

error_msg = "Broadcastnig channel can't be infered. Please, specify it with `from_channel`"
raise ArgumentError, error_msg
end
end
# rubocop: enable Style/ClassLength
end

# @api public
# 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.
# Passes if a message has been sent to a stream/object inside a block.
# May chain `at_least`, `at_most` or `exactly` to specify a number of times.
# To specify channel from which message has been broadcasted to object use `from_channel`.
#
#
# @example
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
# }.to have_broadcasted_to("messages")
#
# expect {
# SomeChannel.broadcast_to(user)
# }.to have_broadcasted_to(user).from_channel(SomeChannel)
#
# # Using alias
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
Expand All @@ -177,9 +206,11 @@ def pubsub_adapter
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
# }.to have_broadcasted_to("messages").with(text: 'Hi!')
def have_broadcasted_to(stream = nil)

def have_broadcasted_to(target = nil)
check_action_cable_adapter
ActionCable::HaveBroadcastedTo.new(stream)

ActionCable::HaveBroadcastedTo.new(target, channel: described_class)
end
alias_method :broadcast_to, :have_broadcasted_to

Expand Down
26 changes: 26 additions & 0 deletions spec/rspec/rails/matchers/action_cable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
require "rspec/rails/matchers/action_cable"
end

class BroadcastToChannel < ActionCable::Channel::Base
end

RSpec.describe "ActionCable matchers", :skip => !RSpec::Rails::FeatureCheck.has_action_cable? do
def broadcast(stream, msg)
ActionCable.server.broadcast stream, msg
Expand Down Expand Up @@ -171,5 +174,28 @@ def broadcast(stream, msg)
expect(e.message).to match(/got: "asdf"/)
}
end


context "when object is passed as first argument" do
let(:user) { User.new(42) }

context "when channel is present" do
it "passes" do
expect {
BroadcastToChannel.broadcast_to(user, text: 'Hi')
}.to have_broadcasted_to(user).from_channel(BroadcastToChannel)
end
end

context "when channel can't be infered" do
it "raises exception" do
expect {
expect {
BroadcastToChannel.broadcast_to(user, text: 'Hi')
}.to have_broadcasted_to(user)
}.to raise_error(ArgumentError)
end
end
end
end
end

0 comments on commit 558af7a

Please sign in to comment.