action-cable-testing 0.4.0 → 0.5.0
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +43 -2
- data/lib/action_cable/testing.rb +11 -16
- data/lib/action_cable/{channel → testing/channel}/test_case.rb +12 -3
- data/lib/action_cable/testing/connection/test_case.rb +242 -0
- data/lib/action_cable/testing/rails_six.rb +35 -0
- data/lib/action_cable/{test_case.rb → testing/test_case.rb} +0 -0
- data/lib/action_cable/{test_helper.rb → testing/test_helper.rb} +14 -2
- data/lib/action_cable/testing/version.rb +1 -1
- data/lib/rspec/rails/example/channel_example_group.rb +1 -0
- data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +1 -1
- metadata +10 -9
- data/lib/action_cable/connection/test_case.rb +0 -201
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9193234672a859c68a8e8d2a68dab7a7ed89b8038f77f3ffd91cd9fc77f4fb8
|
4
|
+
data.tar.gz: e314c6633804c930a8963f9c8b0540214b4e070f9d0c77c29199efc164c1f55f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbe3764f6edf4b3d894b59440c1591a50c6677270102f408314c4fb329e124ae88f903d0ba202125bc59fed96df22e5971734a568a5b84f3a87d59df19d2ecba
|
7
|
+
data.tar.gz: bd252a3a5c11a65606c0da763e53b1a80a88a038e21f231ec902ae2f210d997589414e5b53f77e30ab655cb644a93184e12b3e211a3082160e6ddc6f57c461a5
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.5.0 (2019-02-24)
|
6
|
+
|
7
|
+
- Make compatible with Rails 6. ([@palkan][])
|
8
|
+
|
9
|
+
That allows using RSpec part of the gem with Rails 6
|
10
|
+
(since Action Cable testing will included only in the upcoming RSpec 4).
|
11
|
+
|
12
|
+
- Backport Rails 6.0 API changes. ([@palkan][], [@sponomarev][])
|
13
|
+
|
14
|
+
Some APIs have been deprecated (to be removed in 1.0).
|
15
|
+
|
5
16
|
## 0.4.0 (2019-01-10)
|
6
17
|
|
7
18
|
- Add stream assert methods and matchers. ([@sponomarev][])
|
data/README.md
CHANGED
@@ -4,7 +4,11 @@
|
|
4
4
|
|
5
5
|
This gem provides missing testing utils for [Action Cable][].
|
6
6
|
|
7
|
-
**NOTE:** this gem
|
7
|
+
**NOTE:** this gem [has](https://github.com/rails/rails/pull/33659) [been](https://github.com/rails/rails/pull/33969) [merged](https://github.com/rails/rails/pull/34845) into Rails 6.0.
|
8
|
+
|
9
|
+
If you're using Minitest – you don't need this gem anymore.
|
10
|
+
|
11
|
+
If you're using RSpec < 4, you still can use this gem to write Action Cable specs even for Rails 6.
|
8
12
|
|
9
13
|
|
10
14
|
## Installation
|
@@ -60,6 +64,41 @@ class ExampleTest < ActionDispatch::IntegrationTest
|
|
60
64
|
end
|
61
65
|
```
|
62
66
|
|
67
|
+
If you want to test the broadcasting made with `Channel.broadcast_to`, you should use
|
68
|
+
`Channel.broadcasting_for`\* to generate an underlying stream name and **use Rails 6 compatibility refinement**:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# app/jobs/chat_relay_job.rb
|
72
|
+
class ChatRelayJob < ApplicationJob
|
73
|
+
def perform_later(room, message)
|
74
|
+
ChatChannel.broadcast_to room, text: message
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# test/jobs/chat_relay_job_test.rb
|
80
|
+
require "test_helper"
|
81
|
+
|
82
|
+
# Activate Rails 6 compatible API (for `broadcasting_for`)
|
83
|
+
using ActionCable::Testing::Rails6
|
84
|
+
|
85
|
+
class ChatRelayJobTest < ActiveJob::TestCase
|
86
|
+
include ActionCable::TestHelper
|
87
|
+
|
88
|
+
test "broadcast message to room" do
|
89
|
+
room = rooms(:all)
|
90
|
+
|
91
|
+
assert_broadcast_on(ChatChannel.broadcasting_for(room), text: "Hi!") do
|
92
|
+
ChatRelayJob.perform_now(room, "Hi!")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
\* **NOTE:** in Rails 6.0 you should use `.broadcasting_for`, but it's not backward compatible
|
99
|
+
and we cannot use it in Rails 5.x. See https://github.com/rails/rails/pull/35021.
|
100
|
+
Note also, that this feature hasn't been released in Rails 6.0.0.beta1, so you still need the refinement.
|
101
|
+
|
63
102
|
### Channels Testing
|
64
103
|
|
65
104
|
Channels tests are written as follows:
|
@@ -166,8 +205,10 @@ For example:
|
|
166
205
|
module ApplicationCable
|
167
206
|
class ConnectionTest < ActionCable::Connection::TestCase
|
168
207
|
def test_connects_with_cookies
|
208
|
+
cookies.signed[:user_id] = users[:john].id
|
209
|
+
|
169
210
|
# Simulate a connection
|
170
|
-
connect
|
211
|
+
connect
|
171
212
|
|
172
213
|
# Asserts that the connection identifier is correct
|
173
214
|
assert_equal "John", connection.user.name
|
data/lib/action_cable/testing.rb
CHANGED
@@ -3,24 +3,19 @@
|
|
3
3
|
require "action_cable/testing/version"
|
4
4
|
|
5
5
|
require "action_cable"
|
6
|
+
require "action_cable/testing/rails_six"
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
# These has been merged into Rails 6
|
9
|
+
unless ActionCable::VERSION::MAJOR >= 6
|
10
|
+
require "action_cable/testing/test_helper"
|
11
|
+
require "action_cable/testing/test_case"
|
10
12
|
|
11
|
-
|
12
|
-
eager_autoload do
|
13
|
-
autoload :TestCase
|
14
|
-
end
|
15
|
-
end
|
13
|
+
require "action_cable/testing/channel/test_case"
|
16
14
|
|
17
|
-
|
18
|
-
eager_autoload do
|
19
|
-
autoload :TestCase
|
20
|
-
end
|
21
|
-
end
|
15
|
+
require "action_cable/testing/connection/test_case"
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
# We cannot move subsription adapter under 'testing/' path,
|
18
|
+
# 'cause Action Cable uses this path when resolving an
|
19
|
+
# adapter from its name (in the config.yml)
|
20
|
+
require "action_cable/subscription_adapter/test"
|
26
21
|
end
|
@@ -10,7 +10,7 @@ module ActionCable
|
|
10
10
|
class NonInferrableChannelError < ::StandardError
|
11
11
|
def initialize(name)
|
12
12
|
super "Unable to determine the channel to test from #{name}. " +
|
13
|
-
"You'll need to specify it using tests YourChannel in your " +
|
13
|
+
"You'll need to specify it using `tests YourChannel` in your " +
|
14
14
|
"test case definition."
|
15
15
|
end
|
16
16
|
end
|
@@ -150,14 +150,13 @@ module ActionCable
|
|
150
150
|
|
151
151
|
include ActiveSupport::Testing::ConstantLookup
|
152
152
|
include ActionCable::TestHelper
|
153
|
-
include ActionCable::Connection::TestCase::Behavior
|
154
153
|
|
155
154
|
CHANNEL_IDENTIFIER = "test_stub"
|
156
155
|
|
157
156
|
included do
|
158
157
|
class_attribute :_channel_class
|
159
158
|
|
160
|
-
attr_reader :subscription
|
159
|
+
attr_reader :connection, :subscription
|
161
160
|
|
162
161
|
def streams
|
163
162
|
ActiveSupport::Deprecation.warn "Use appropriate `assert_has_stream`, `assert_has_stream_for`, `assert_no_streams` " +
|
@@ -242,6 +241,16 @@ module ActionCable
|
|
242
241
|
connection.transmissions.map { |data| data["message"] }.compact
|
243
242
|
end
|
244
243
|
|
244
|
+
# Enhance TestHelper assertions to handle non-String
|
245
|
+
# broadcastings
|
246
|
+
def assert_broadcasts(stream_or_object, *args)
|
247
|
+
super(broadcasting_for(stream_or_object), *args)
|
248
|
+
end
|
249
|
+
|
250
|
+
def assert_broadcast_on(stream_or_object, *args)
|
251
|
+
super(broadcasting_for(stream_or_object), *args)
|
252
|
+
end
|
253
|
+
|
245
254
|
# Asserts that no streams have been started.
|
246
255
|
#
|
247
256
|
# def test_assert_no_started_stream
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/test_case"
|
5
|
+
require "active_support/core_ext/hash/indifferent_access"
|
6
|
+
require "action_dispatch"
|
7
|
+
require "action_dispatch/http/headers"
|
8
|
+
require "action_dispatch/testing/test_request"
|
9
|
+
|
10
|
+
module ActionCable
|
11
|
+
module Connection
|
12
|
+
class NonInferrableConnectionError < ::StandardError
|
13
|
+
def initialize(name)
|
14
|
+
super "Unable to determine the connection to test from #{name}. " +
|
15
|
+
"You'll need to specify it using `tests YourConnection` in your " +
|
16
|
+
"test case definition."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Assertions
|
21
|
+
# Asserts that the connection is rejected (via +reject_unauthorized_connection+).
|
22
|
+
#
|
23
|
+
# # Asserts that connection without user_id fails
|
24
|
+
# assert_reject_connection { connect cookies: { user_id: '' } }
|
25
|
+
def assert_reject_connection(&block)
|
26
|
+
assert_raises(Authorization::UnauthorizedError, "Expected to reject connection but no rejection was made", &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# We don't want to use the whole "encryption stack" for connection
|
31
|
+
# unit-tests, but we want to make sure that users test against the correct types
|
32
|
+
# of cookies (i.e. signed or encrypted or plain)
|
33
|
+
class TestCookieJar < ActiveSupport::HashWithIndifferentAccess
|
34
|
+
def signed
|
35
|
+
self[:signed] ||= {}.with_indifferent_access
|
36
|
+
end
|
37
|
+
|
38
|
+
def encrypted
|
39
|
+
self[:encrypted] ||= {}.with_indifferent_access
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class TestRequest < ActionDispatch::TestRequest
|
44
|
+
attr_accessor :session, :cookie_jar
|
45
|
+
end
|
46
|
+
|
47
|
+
module TestConnection
|
48
|
+
attr_reader :logger, :request
|
49
|
+
|
50
|
+
def initialize(request)
|
51
|
+
inner_logger = ActiveSupport::Logger.new(StringIO.new)
|
52
|
+
tagged_logging = ActiveSupport::TaggedLogging.new(inner_logger)
|
53
|
+
@logger = ActionCable::Connection::TaggedLoggerProxy.new(tagged_logging, tags: [])
|
54
|
+
@request = request
|
55
|
+
@env = request.env
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Unit test Action Cable connections.
|
60
|
+
#
|
61
|
+
# Useful to check whether a connection's +identified_by+ gets assigned properly
|
62
|
+
# and that any improper connection requests are rejected.
|
63
|
+
#
|
64
|
+
# == Basic example
|
65
|
+
#
|
66
|
+
# Unit tests are written as follows:
|
67
|
+
#
|
68
|
+
# 1. Simulate a connection attempt by calling +connect+.
|
69
|
+
# 2. Assert state, e.g. identifiers, has been assigned.
|
70
|
+
#
|
71
|
+
#
|
72
|
+
# class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
|
73
|
+
# def test_connects_with_proper_cookie
|
74
|
+
# # Simulate the connection request with a cookie.
|
75
|
+
# cookies["user_id"] = users(:john).id
|
76
|
+
#
|
77
|
+
# connect
|
78
|
+
#
|
79
|
+
# # Assert the connection identifier matches the fixture.
|
80
|
+
# assert_equal users(:john).id, connection.user.id
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# def test_rejects_connection_without_proper_cookie
|
84
|
+
# assert_reject_connection { connect }
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# +connect+ accepts additional information the HTTP request with the
|
89
|
+
# +params+, +headers+, +session+ and Rack +env+ options.
|
90
|
+
#
|
91
|
+
# def test_connect_with_headers_and_query_string
|
92
|
+
# connect params: { user_id: 1 }, headers: { "X-API-TOKEN" => "secret-my" }
|
93
|
+
#
|
94
|
+
# assert_equal "1", connection.user.id
|
95
|
+
# assert_equal "secret-my", connection.token
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# def test_connect_with_params
|
99
|
+
# connect params: { user_id: 1 }
|
100
|
+
#
|
101
|
+
# assert_equal "1", connection.user.id
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# You can also setup the correct cookies before the connection request:
|
105
|
+
#
|
106
|
+
# def test_connect_with_cookies
|
107
|
+
# # Plain cookies:
|
108
|
+
# cookies["user_id"] = 1
|
109
|
+
#
|
110
|
+
# # Or signed/encrypted:
|
111
|
+
# # cookies.signed["user_id"] = 1
|
112
|
+
# # cookies.encrypted["user_id"] = 1
|
113
|
+
#
|
114
|
+
# connect
|
115
|
+
#
|
116
|
+
# assert_equal "1", connection.user_id
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# == Connection is automatically inferred
|
120
|
+
#
|
121
|
+
# ActionCable::Connection::TestCase will automatically infer the connection under test
|
122
|
+
# from the test class name. If the channel cannot be inferred from the test
|
123
|
+
# class name, you can explicitly set it with +tests+.
|
124
|
+
#
|
125
|
+
# class ConnectionTest < ActionCable::Connection::TestCase
|
126
|
+
# tests ApplicationCable::Connection
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
class TestCase < ActiveSupport::TestCase
|
130
|
+
module Behavior
|
131
|
+
extend ActiveSupport::Concern
|
132
|
+
|
133
|
+
DEFAULT_PATH = "/cable"
|
134
|
+
|
135
|
+
include ActiveSupport::Testing::ConstantLookup
|
136
|
+
include Assertions
|
137
|
+
|
138
|
+
included do
|
139
|
+
class_attribute :_connection_class
|
140
|
+
|
141
|
+
attr_reader :connection
|
142
|
+
|
143
|
+
ActiveSupport.run_load_hooks(:action_cable_connection_test_case, self)
|
144
|
+
end
|
145
|
+
|
146
|
+
module ClassMethods
|
147
|
+
def tests(connection)
|
148
|
+
case connection
|
149
|
+
when String, Symbol
|
150
|
+
self._connection_class = connection.to_s.camelize.constantize
|
151
|
+
when Module
|
152
|
+
self._connection_class = connection
|
153
|
+
else
|
154
|
+
raise NonInferrableConnectionError.new(connection)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def connection_class
|
159
|
+
if connection = self._connection_class
|
160
|
+
connection
|
161
|
+
else
|
162
|
+
tests determine_default_connection(name)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def determine_default_connection(name)
|
167
|
+
connection = determine_constant_from_test_name(name) do |constant|
|
168
|
+
Class === constant && constant < ActionCable::Connection::Base
|
169
|
+
end
|
170
|
+
raise NonInferrableConnectionError.new(name) if connection.nil?
|
171
|
+
connection
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Performs connection attempt to exert #connect on the connection under test.
|
176
|
+
#
|
177
|
+
# Accepts request path as the first argument and the following request options:
|
178
|
+
#
|
179
|
+
# - params – url parameters (Hash)
|
180
|
+
# - headers – request headers (Hash)
|
181
|
+
# - session – session data (Hash)
|
182
|
+
# - env – additional Rack env configuration (Hash)
|
183
|
+
def connect(path = ActionCable.server.config.mount_path, cookies: nil, **request_params)
|
184
|
+
path ||= DEFAULT_PATH
|
185
|
+
|
186
|
+
unless cookies.nil?
|
187
|
+
ActiveSupport::Deprecation.warn(
|
188
|
+
"Use `cookies[:param] = value` (or `cookies.signed[:param] = value`). " \
|
189
|
+
"Passing `cookies` as the `connect` option is deprecated and is going to be removed in version 1.0"
|
190
|
+
)
|
191
|
+
cookies.each { |k, v| self.cookies[k] = v }
|
192
|
+
end
|
193
|
+
|
194
|
+
connection = self.class.connection_class.allocate
|
195
|
+
connection.singleton_class.include(TestConnection)
|
196
|
+
connection.send(:initialize, build_test_request(path, request_params))
|
197
|
+
connection.connect if connection.respond_to?(:connect)
|
198
|
+
|
199
|
+
# Only set instance variable if connected successfully
|
200
|
+
@connection = connection
|
201
|
+
end
|
202
|
+
|
203
|
+
# Exert #disconnect on the connection under test.
|
204
|
+
def disconnect
|
205
|
+
raise "Must be connected!" if connection.nil?
|
206
|
+
|
207
|
+
connection.disconnect if connection.respond_to?(:disconnect)
|
208
|
+
@connection = nil
|
209
|
+
end
|
210
|
+
|
211
|
+
def cookies
|
212
|
+
@cookie_jar ||= TestCookieJar.new
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
def build_test_request(path, params: nil, headers: {}, session: {}, env: {})
|
217
|
+
wrapped_headers = ActionDispatch::Http::Headers.from_hash(headers)
|
218
|
+
|
219
|
+
uri = URI.parse(path)
|
220
|
+
|
221
|
+
query_string = params.nil? ? uri.query : params.to_query
|
222
|
+
|
223
|
+
request_env = {
|
224
|
+
"QUERY_STRING" => query_string,
|
225
|
+
"PATH_INFO" => uri.path
|
226
|
+
}.merge(env)
|
227
|
+
|
228
|
+
if wrapped_headers.present?
|
229
|
+
ActionDispatch::Http::Headers.from_hash(request_env).merge!(wrapped_headers)
|
230
|
+
end
|
231
|
+
|
232
|
+
TestRequest.create(request_env).tap do |request|
|
233
|
+
request.session = session.with_indifferent_access
|
234
|
+
request.cookie_jar = cookies
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
include Behavior
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionCable
|
4
|
+
module Testing
|
5
|
+
# Enables Rails 6 compatible API via Refinements.
|
6
|
+
#
|
7
|
+
# Use this to write Rails 6 compatible tests (not every Rails 6 API
|
8
|
+
# could be backported).
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# # my_test.rb
|
12
|
+
# require "test_helper"
|
13
|
+
#
|
14
|
+
# using ActionCable::Testing::Syntax
|
15
|
+
module Rails6
|
16
|
+
begin
|
17
|
+
# Has been added only after 6.0.0.beta1
|
18
|
+
unless ActionCable::Channel.respond_to?(:serialize_broadcasting)
|
19
|
+
refine ActionCable::Channel::Broadcasting::ClassMethods do
|
20
|
+
def broadcasting_for(model)
|
21
|
+
super([channel_name, model])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
SUPPORTED = true
|
27
|
+
rescue TypeError
|
28
|
+
warn "Your Ruby version doesn't suppport Module refinements. " \
|
29
|
+
"Rails 6 compatibility refinement could not be applied"
|
30
|
+
|
31
|
+
SUPPORTED = false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
File without changes
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActionCable
|
4
4
|
# Provides helper methods for testing Action Cable broadcasting
|
5
5
|
module TestHelper
|
6
|
-
CHANNEL_NOT_FOUND = ArgumentError.new("
|
6
|
+
CHANNEL_NOT_FOUND = ArgumentError.new("Broadcasting channel can't be infered. Please, specify it with `:channel`")
|
7
7
|
|
8
8
|
def before_setup # :nodoc:
|
9
9
|
server = ActionCable.server
|
@@ -45,6 +45,8 @@ module ActionCable
|
|
45
45
|
# end
|
46
46
|
#
|
47
47
|
def assert_broadcasts(target, number, channel: nil)
|
48
|
+
warn_deprecated_channel! unless channel.nil?
|
49
|
+
|
48
50
|
stream = stream(target, channel)
|
49
51
|
|
50
52
|
if block_given?
|
@@ -98,6 +100,8 @@ module ActionCable
|
|
98
100
|
# end
|
99
101
|
#
|
100
102
|
def assert_broadcast_on(target, data, channel: nil)
|
103
|
+
warn_deprecated_channel! unless channel.nil?
|
104
|
+
|
101
105
|
# Encode to JSON and back–we want to use this value to compare
|
102
106
|
# with decoded JSON.
|
103
107
|
# Comparing JSON strings doesn't work due to the order if the keys.
|
@@ -138,9 +142,17 @@ module ActionCable
|
|
138
142
|
return target if target.is_a?(String)
|
139
143
|
|
140
144
|
channel ||= @subscription
|
141
|
-
|
145
|
+
return target unless channel && channel.respond_to?(:channel_name)
|
142
146
|
|
143
147
|
channel.broadcasting_for([channel.channel_name, target])
|
144
148
|
end
|
149
|
+
|
150
|
+
def warn_deprecated_channel!
|
151
|
+
ActiveSupport::Deprecation.warn(
|
152
|
+
"Passing channel class is deprecated and will be removed in version 1.0. " \
|
153
|
+
"Use `Channel.broadcasting_for(object) to build a stream name instead and " \
|
154
|
+
"add `using ActionCable::Testing::Rails6` to your test file."
|
155
|
+
)
|
156
|
+
end
|
145
157
|
end
|
146
158
|
end
|
@@ -21,6 +21,7 @@ if defined?(ActionCable)
|
|
21
21
|
module ChannelExampleGroup
|
22
22
|
extend ActiveSupport::Concern
|
23
23
|
include RSpec::Rails::RailsExampleGroup
|
24
|
+
include ActionCable::Connection::TestCase::Behavior
|
24
25
|
include ActionCable::Channel::TestCase::Behavior
|
25
26
|
|
26
27
|
# Class-level DSL for channel specs.
|
@@ -160,7 +160,7 @@ module RSpec
|
|
160
160
|
def check_channel_presence
|
161
161
|
return if @channel.present? && @channel.respond_to?(:channel_name)
|
162
162
|
|
163
|
-
error_msg = "
|
163
|
+
error_msg = "Broadcasting channel can't be infered. Please, specify it with `from_channel`"
|
164
164
|
raise ArgumentError, error_msg
|
165
165
|
end
|
166
166
|
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
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.5.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: 2019-
|
11
|
+
date: 2019-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actioncable
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -147,14 +147,15 @@ files:
|
|
147
147
|
- LICENSE.txt
|
148
148
|
- README.md
|
149
149
|
- lib/action-cable-testing.rb
|
150
|
-
- lib/action_cable/channel/test_case.rb
|
151
|
-
- lib/action_cable/connection/test_case.rb
|
152
150
|
- lib/action_cable/subscription_adapter/test.rb
|
153
|
-
- lib/action_cable/test_case.rb
|
154
|
-
- lib/action_cable/test_helper.rb
|
155
151
|
- lib/action_cable/testing.rb
|
152
|
+
- lib/action_cable/testing/channel/test_case.rb
|
153
|
+
- lib/action_cable/testing/connection/test_case.rb
|
154
|
+
- lib/action_cable/testing/rails_six.rb
|
156
155
|
- lib/action_cable/testing/rspec.rb
|
157
156
|
- lib/action_cable/testing/rspec/features.rb
|
157
|
+
- lib/action_cable/testing/test_case.rb
|
158
|
+
- lib/action_cable/testing/test_helper.rb
|
158
159
|
- lib/action_cable/testing/version.rb
|
159
160
|
- lib/generators/rspec/channel/channel_generator.rb
|
160
161
|
- lib/generators/rspec/channel/templates/channel_spec.rb.erb
|
@@ -177,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
177
178
|
requirements:
|
178
179
|
- - ">="
|
179
180
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
181
|
+
version: 2.3.0
|
181
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
183
|
requirements:
|
183
184
|
- - ">="
|
@@ -1,201 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_support"
|
4
|
-
require "active_support/test_case"
|
5
|
-
require "active_support/core_ext/hash/indifferent_access"
|
6
|
-
require "action_dispatch"
|
7
|
-
require "action_dispatch/testing/test_request"
|
8
|
-
|
9
|
-
module ActionCable
|
10
|
-
module Connection
|
11
|
-
class NonInferrableConnectionError < ::StandardError
|
12
|
-
def initialize(name)
|
13
|
-
super "Unable to determine the connection to test from #{name}. " +
|
14
|
-
"You'll need to specify it using tests YourConnection in your " +
|
15
|
-
"test case definition."
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Assertions
|
20
|
-
# Asserts that the connection is rejected (via +reject_unauthorized_connection+).
|
21
|
-
#
|
22
|
-
# # Asserts that connection without user_id fails
|
23
|
-
# assert_reject_connection { connect cookies: { user_id: '' } }
|
24
|
-
def assert_reject_connection(&block)
|
25
|
-
res =
|
26
|
-
begin
|
27
|
-
block.call
|
28
|
-
false
|
29
|
-
rescue ActionCable::Connection::Authorization::UnauthorizedError
|
30
|
-
true
|
31
|
-
end
|
32
|
-
|
33
|
-
assert res, "Expected to reject connection but no rejection were made"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class TestRequest < ActionDispatch::TestRequest
|
38
|
-
attr_reader :cookie_jar
|
39
|
-
attr_accessor :session
|
40
|
-
|
41
|
-
module CookiesStub
|
42
|
-
# Stub signed cookies
|
43
|
-
def signed
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
# Stub encrypted cookies
|
48
|
-
def encrypted
|
49
|
-
self
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def cookie_jar=(val)
|
54
|
-
@cookie_jar = val.tap do |h|
|
55
|
-
h.singleton_class.include(CookiesStub)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
module TestConnection
|
61
|
-
attr_reader :logger, :request
|
62
|
-
|
63
|
-
def initialize(path, cookies, headers, session)
|
64
|
-
inner_logger = ActiveSupport::Logger.new(StringIO.new)
|
65
|
-
tagged_logging = ActiveSupport::TaggedLogging.new(inner_logger)
|
66
|
-
@logger = ActionCable::Connection::TaggedLoggerProxy.new(tagged_logging, tags: [])
|
67
|
-
|
68
|
-
uri = URI.parse(path)
|
69
|
-
env = {
|
70
|
-
"QUERY_STRING" => uri.query,
|
71
|
-
"PATH_INFO" => uri.path
|
72
|
-
}.merge(build_headers(headers))
|
73
|
-
|
74
|
-
@request = TestRequest.create(env)
|
75
|
-
@request.cookie_jar = cookies.with_indifferent_access
|
76
|
-
@request.session = session.with_indifferent_access
|
77
|
-
end
|
78
|
-
|
79
|
-
def build_headers(headers)
|
80
|
-
headers.each_with_object({}) do |(k, v), obj|
|
81
|
-
k = k.upcase
|
82
|
-
k.tr!("-", "_")
|
83
|
-
obj["HTTP_#{k}"] = v
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Superclass for Action Cable connection unit tests.
|
89
|
-
#
|
90
|
-
# == Basic example
|
91
|
-
#
|
92
|
-
# Unit tests are written as follows:
|
93
|
-
# 1. First, one uses the +connect+ method to simulate connection.
|
94
|
-
# 2. Then, one asserts whether the current state is as expected (e.g. identifiers).
|
95
|
-
#
|
96
|
-
# For example:
|
97
|
-
#
|
98
|
-
# module ApplicationCable
|
99
|
-
# class ConnectionTest < ActionCable::Connection::TestCase
|
100
|
-
# def test_connects_with_cookies
|
101
|
-
# # Simulate a connection
|
102
|
-
# connect cookies: { user_id: users[:john].id }
|
103
|
-
#
|
104
|
-
# # Asserts that the connection identifier is correct
|
105
|
-
# assert_equal "John", connection.user.name
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
# def test_does_not_connect_without_user
|
109
|
-
# assert_reject_connection do
|
110
|
-
# connect
|
111
|
-
# end
|
112
|
-
# end
|
113
|
-
# end
|
114
|
-
#
|
115
|
-
# You can also provide additional information about underlying HTTP request:
|
116
|
-
# def test_connect_with_headers_and_query_string
|
117
|
-
# connect "/cable?user_id=1", headers: { "X-API-TOKEN" => 'secret-my' }
|
118
|
-
#
|
119
|
-
# assert_equal connection.user_id, "1"
|
120
|
-
# end
|
121
|
-
#
|
122
|
-
# == Connection is automatically inferred
|
123
|
-
#
|
124
|
-
# ActionCable::Connection::TestCase will automatically infer the connection under test
|
125
|
-
# from the test class name. If the channel cannot be inferred from the test
|
126
|
-
# class name, you can explicitly set it with +tests+.
|
127
|
-
#
|
128
|
-
# class ConnectionTest < ActionCable::Connection::TestCase
|
129
|
-
# tests ApplicationCable::Connection
|
130
|
-
# end
|
131
|
-
#
|
132
|
-
class TestCase < ActiveSupport::TestCase
|
133
|
-
module Behavior
|
134
|
-
extend ActiveSupport::Concern
|
135
|
-
|
136
|
-
include ActiveSupport::Testing::ConstantLookup
|
137
|
-
include Assertions
|
138
|
-
|
139
|
-
included do
|
140
|
-
class_attribute :_connection_class
|
141
|
-
|
142
|
-
attr_reader :connection
|
143
|
-
|
144
|
-
ActiveSupport.run_load_hooks(:action_cable_connection_test_case, self)
|
145
|
-
end
|
146
|
-
|
147
|
-
module ClassMethods
|
148
|
-
def tests(connection)
|
149
|
-
case connection
|
150
|
-
when String, Symbol
|
151
|
-
self._connection_class = connection.to_s.camelize.constantize
|
152
|
-
when Module
|
153
|
-
self._connection_class = connection
|
154
|
-
else
|
155
|
-
raise NonInferrableConnectionError.new(connection)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def connection_class
|
160
|
-
if connection = self._connection_class
|
161
|
-
connection
|
162
|
-
else
|
163
|
-
tests determine_default_connection(name)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def determine_default_connection(name)
|
168
|
-
connection = determine_constant_from_test_name(name) do |constant|
|
169
|
-
Class === constant && constant < ActionCable::Connection::Base
|
170
|
-
end
|
171
|
-
raise NonInferrableConnectionError.new(name) if connection.nil?
|
172
|
-
connection
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Performs connection attempt (i.e. calls #connect method).
|
177
|
-
#
|
178
|
-
# Accepts request path as the first argument and cookies and headers as options.
|
179
|
-
def connect(path = "/cable", cookies: {}, headers: {}, session: {})
|
180
|
-
connection = self.class.connection_class.allocate
|
181
|
-
connection.singleton_class.include(TestConnection)
|
182
|
-
connection.send(:initialize, path, cookies, headers, session)
|
183
|
-
connection.connect if connection.respond_to?(:connect)
|
184
|
-
|
185
|
-
# Only set instance variable if connected successfully
|
186
|
-
@connection = connection
|
187
|
-
end
|
188
|
-
|
189
|
-
# Disconnect the connection under test (i.e. calls #disconnect)
|
190
|
-
def disconnect
|
191
|
-
raise "Must be connected!" if connection.nil?
|
192
|
-
|
193
|
-
connection.disconnect if connection.respond_to?(:disconnect)
|
194
|
-
@connection = nil
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
include Behavior
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|