Skip to content

Commit

Permalink
Add have_broadcasted_to matcher
Browse files Browse the repository at this point in the history
action-cable-testing migration, pt.1
  • Loading branch information
palkan authored and benoittgt committed May 15, 2019
1 parent 81d0d60 commit 5bf3d13
Show file tree
Hide file tree
Showing 11 changed files with 643 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ Cucumber::Rake::Task.new(:cucumber) do |t|
tags << "~@system_test"
end

if version.to_f < 6.0
tags << "~@rails_post_6"
end

cucumber_flag = tags.map { |tag| "--tag #{tag}" }

t.cucumber_opts = cucumber_flag
Expand Down
7 changes: 7 additions & 0 deletions example_app_generator/generate_stuff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ def using_source_path(path)
rescue LoadError
end

begin
require 'action_cable'
require 'action_cable/test_helper'
generate('channel chat')
rescue LoadError
end

file "app/views/things/custom_action.html.erb",
"This is a template for a custom action.",
:force => true
Expand Down
151 changes: 151 additions & 0 deletions features/matchers/have_broadcasted_matcher.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
@rails_post_6
Feature: have_broadcasted matcher

The `have_broadcasted_to` (also aliased as `broadcast_to`) matcher is used to check if a message has been broadcasted to a given stream.

Background:
Given action cable testing is available

Scenario: Checking stream name
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with stream name" do
expect {
ActionCable.server.broadcast(
"notifications", text: 'Hello!'
)
}.to have_broadcasted_to("notifications")
end
end
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the examples should all pass

Scenario: Checking passed message to stream
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with message" do
expect {
ActionCable.server.broadcast(
"notifications", text: 'Hello!'
)
}.to have_broadcasted_to("notifications").with(text: 'Hello!')
end
end
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the examples should all pass

Scenario: Checking that message passed to stream matches
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with message" do
expect {
ActionCable.server.broadcast(
"notifications", text: 'Hello!', user_id: 12
)
}.to have_broadcasted_to("notifications").with(a_hash_including(text: 'Hello!'))
end
end
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the examples should all pass

Scenario: Checking passed message with block
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with message" do
expect {
ActionCable.server.broadcast(
"notifications", text: 'Hello!', user_id: 12
)
}.to have_broadcasted_to("notifications").with { |data|
expect(data['user_id']).to eq 12
}
end
end
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the examples should all pass

Scenario: Using alias method
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with stream name" do
expect {
ActionCable.server.broadcast(
"notifications", text: 'Hello!'
)
}.to broadcast_to("notifications")
end
end
"""
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_gid_param
name
end
end
"""
When I run `rspec spec/channels/chat_channel_spec.rb`
Then the example should pass

Scenario: Checking broadcast to a record in non-channel spec
Given a file named "spec/models/broadcaster_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe "broadcasting" do
it "matches with stream name" do
user = User.new(42)
expect {
ChatChannel.broadcast_to(user, text: 'Hi')
}.to broadcast_to(ChatChannel.broadcasting_for(user))
end
end
"""
And a file named "app/models/user.rb" with:
"""ruby
class User < Struct.new(:name)
def to_gid_param
name
end
end
"""
When I run `rspec spec/models/broadcaster_spec.rb`
Then the example should pass
11 changes: 11 additions & 0 deletions features/step_definitions/additional_cli_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
require "active_job"
rescue LoadError # rubocop:disable Lint/HandleExceptions
end
begin
require "action_cable"
rescue LoadError # rubocop:disable Lint/HandleExceptions
end

require "rails/version"

require "rspec/rails/feature_check"
Expand All @@ -27,3 +32,9 @@
pending "file fixtures are not available"
end
end

Given /action cable testing is available/ do
if !RSpec::Rails::FeatureCheck.has_action_cable_testing?
pending "Action Cable testing is not available"
end
end
1 change: 1 addition & 0 deletions lib/rspec/rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Configuration
#
# @api private
DIRECTORY_MAPPINGS = {
:channel => %w[spec channels],
:controller => %w[spec controllers],
:helper => %w[spec helpers],
:job => %w[spec jobs],
Expand Down
4 changes: 4 additions & 0 deletions lib/rspec/rails/feature_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def has_action_mailer_preview?
has_action_mailer? && defined?(::ActionMailer::Preview)
end

def has_action_cable_testing?
defined?(::ActionCable) && ActionCable::VERSION::MAJOR >= 6
end

def has_action_mailer_show_preview?
has_action_mailer_preview? &&
::ActionMailer::Base.respond_to?(:show_previews=)
Expand Down
4 changes: 4 additions & 0 deletions lib/rspec/rails/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ module Matchers
require 'rspec/rails/matchers/active_job'
end

if RSpec::Rails::FeatureCheck.has_action_cable_testing?
require 'rspec/rails/matchers/action_cable'
end

if RSpec::Rails::FeatureCheck.has_action_mailbox?
require 'rspec/rails/matchers/action_mailbox'
end
65 changes: 65 additions & 0 deletions lib/rspec/rails/matchers/action_cable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "rspec/rails/matchers/action_cable/have_broadcasted_to"

module RSpec
module Rails
module Matchers
# Namespace for various implementations of ActionCable features
#
# @api private
module ActionCable
end

# @api public
# 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!'
# }.to broadcast_to("messages")
#
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
# ActionCable.server.broadcast "all", text: 'Hi!'
# }.to have_broadcasted_to("messages").exactly(:once)
#
# expect {
# 3.times { ActionCable.server.broadcast "messages", text: 'Hi!' }
# }.to have_broadcasted_to("messages").at_least(2).times
#
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
# }.to have_broadcasted_to("messages").at_most(:twice)
#
# expect {
# ActionCable.server.broadcast "messages", text: 'Hi!'
# }.to have_broadcasted_to("messages").with(text: 'Hi!')
def have_broadcasted_to(target = nil)
check_action_cable_adapter

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

private

# @private
def check_action_cable_adapter
return if ::ActionCable::SubscriptionAdapter::Test === ::ActionCable.server.pubsub

raise StandardError, "To use ActionCable matchers set `adapter: test` in your cable.yml"
end
end
end
end
Loading

0 comments on commit 5bf3d13

Please sign in to comment.