class RSpec::Rails::Matchers::ActionCable::HaveBroadcastedTo
@private
rubocop: disable Metrics/ClassLength
def at_least(count)
def at_least(count) set_expected_number(:at_least, count) self end
def at_most(count)
def at_most(count) set_expected_number(:at_most, count) self end
def base_description
def base_description "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg| msg << " with #{data_description(@data)}" unless @data.nil? end end
def base_message
def base_message "#{base_description}, but broadcast #{@matching_msgs_count}" end
def check(messages)
def check(messages) @matching_msgs, @unmatching_msgs = messages.partition do |msg| decoded = ActiveSupport::JSON.decode(msg) decoded = decoded.with_indifferent_access if decoded.is_a?(Hash) if @data.nil? || values_match?(@data, decoded) @block.call(decoded) true else false end end @matching_msgs_count = @matching_msgs.size case @expectation_type when :exactly then @expected_number == @matching_msgs_count when :at_most then @expected_number >= @matching_msgs_count when :at_least then @expected_number <= @matching_msgs_count end end
def check_channel_presence
def check_channel_presence return if @channel.present? && @channel.respond_to?(:channel_name) error_msg = "Broadcasting channel can't be inferred. Please, specify it with `from_channel`" raise ArgumentError, error_msg end
def data_description(data)
def data_description(data) if data.is_a?(RSpec::Matchers::Composable) data.description else data.inspect end end
def description
def description "have broadcasted #{base_description}" end
def exactly(count)
def exactly(count) set_expected_number(:exactly, count) self end
def failure_message
def failure_message "expected to broadcast #{base_message}".tap do |msg| if @unmatching_msgs.any? msg << "\nBroadcasted messages to #{stream}:" @unmatching_msgs.each do |data| msg << "\n #{data}" end end end end
def failure_message_when_negated
def failure_message_when_negated "expected not to broadcast #{base_message}" end
def from_channel(channel)
def from_channel(channel) @channel = channel self end
def initialize(target, channel:)
def initialize(target, channel:) @target = target @channel = channel @block = proc { } @data = nil set_expected_number(:exactly, 1) end
def matches?(proc)
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 proc.call in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count) check(in_block_messages) end
def message_expectation_modifier
def message_expectation_modifier case @expectation_type when :exactly then "exactly" when :at_most then "at most" when :at_least then "at least" end end
def once
def once exactly(:once) end
def pubsub_adapter
def pubsub_adapter ::ActionCable.server.pubsub end
def set_expected_number(relativity, count)
def set_expected_number(relativity, count) @expectation_type = relativity @expected_number = case count when :once then 1 when :twice then 2 when :thrice then 3 else Integer(count) end end
def stream
def stream @stream ||= case @target when String @target when Symbol @target.to_s else check_channel_presence @channel.broadcasting_for(@target) end end
def supports_block_expectations?
def supports_block_expectations? true end
def thrice
def thrice exactly(:thrice) end
def times
def times self end
def twice
def twice exactly(:twice) end
def with(data = nil, &block)
def with(data = nil, &block) @data = data @data = @data.with_indifferent_access if @data.is_a?(Hash) @block = block if block self end