render_turbo_stream 3.0.5 → 4.0.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/README.md +36 -92
- data/app/views/layouts/_add_turbo_frame_tag.html.erb +6 -0
- data/app/views/render_turbo_stream.turbo_stream.erb +13 -4
- data/app/views/render_turbo_stream_request_test.html.erb +13 -5
- data/lib/render_turbo_stream/channel_libs.rb +58 -14
- data/lib/render_turbo_stream/channel_view_helpers.rb +8 -0
- data/lib/render_turbo_stream/check_template.rb +122 -0
- data/lib/render_turbo_stream/controller_channel_helpers.rb +102 -10
- data/lib/render_turbo_stream/controller_helpers.rb +79 -153
- data/lib/render_turbo_stream/controller_libs.rb +162 -0
- data/lib/render_turbo_stream/libs.rb +35 -0
- data/lib/render_turbo_stream/test/request/channel_helpers.rb +50 -10
- data/lib/render_turbo_stream/test/request/helpers.rb +22 -10
- data/lib/render_turbo_stream/test/request/libs.rb +97 -42
- data/lib/render_turbo_stream/version.rb +1 -1
- data/lib/render_turbo_stream.rb +3 -0
- metadata +13 -9
@@ -9,29 +9,57 @@ module RenderTurboStream
|
|
9
9
|
|
10
10
|
channel = "authenticated-user-#{user.id}"
|
11
11
|
|
12
|
-
libs = RenderTurboStream::Test::Request::Libs
|
12
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
13
13
|
|
14
|
-
r = libs.select_responses(
|
14
|
+
r = libs.select_responses(channel, RenderTurboStream::Libs.target_id_to_target(target_id), action, &block)
|
15
15
|
|
16
16
|
assert(
|
17
17
|
r[:responses].length == count,
|
18
|
-
libs.assert_error_message(count, r[:responses].length, r[:log])
|
18
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_action_to_me(user, action, count: 1, &block)
|
23
|
+
|
24
|
+
channel = "authenticated-user-#{user.id}"
|
25
|
+
|
26
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
27
|
+
|
28
|
+
r = libs.select_actions(channel, action, &block)
|
29
|
+
|
30
|
+
assert(
|
31
|
+
r[:responses].length == count,
|
32
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
19
33
|
)
|
20
34
|
end
|
21
35
|
|
22
36
|
# Assert a action by turbo streams channel to a group of authenticated users
|
23
37
|
|
24
|
-
def assert_channel_to_authenticated_group(group, target_id,
|
38
|
+
def assert_channel_to_authenticated_group(group, target_id, count: 1, &block)
|
39
|
+
|
40
|
+
channel = "authenticated-group-#{group}"
|
41
|
+
|
42
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
43
|
+
|
44
|
+
r = libs.select_responses(channel, target_id, action, &block)
|
45
|
+
|
46
|
+
assert(
|
47
|
+
r[:responses].length == count,
|
48
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def assert_action_to_authenticated_group(group, action, count: 1, &block)
|
25
53
|
|
26
54
|
channel = "authenticated-group-#{group}"
|
27
55
|
|
28
|
-
libs = RenderTurboStream::Test::Request::Libs
|
56
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
29
57
|
|
30
|
-
r = libs.
|
58
|
+
r = libs.select_actions(channel, action, &block)
|
31
59
|
|
32
60
|
assert(
|
33
61
|
r[:responses].length == count,
|
34
|
-
libs.assert_error_message(count, r[:responses].length, r[:log])
|
62
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
35
63
|
)
|
36
64
|
end
|
37
65
|
|
@@ -39,13 +67,25 @@ module RenderTurboStream
|
|
39
67
|
|
40
68
|
def assert_channel_to_all(target_id, action: nil, count: 1, &block)
|
41
69
|
|
42
|
-
libs = RenderTurboStream::Test::Request::Libs
|
70
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
71
|
+
|
72
|
+
r = libs.select_responses('all', RenderTurboStream::Libs.target_id_to_target(target_id), action, &block)
|
73
|
+
|
74
|
+
assert(
|
75
|
+
r[:responses].length == count,
|
76
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def assert_action_to_all(action, count: 1, &block)
|
81
|
+
|
82
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
43
83
|
|
44
|
-
r = libs.
|
84
|
+
r = libs.select_actions('all', action, &block)
|
45
85
|
|
46
86
|
assert(
|
47
87
|
r[:responses].length == count,
|
48
|
-
libs.assert_error_message(count, r[:responses].length, r[:log])
|
88
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
49
89
|
)
|
50
90
|
end
|
51
91
|
|
@@ -6,7 +6,7 @@ module RenderTurboStream
|
|
6
6
|
# Assert that each of given target_ids is targeted exactly once
|
7
7
|
|
8
8
|
def assert_once_targeted(*target_ids)
|
9
|
-
responses = RenderTurboStream::Test::Request::Libs.
|
9
|
+
responses = RenderTurboStream::Test::Request::Libs.new(response).all_turbo_responses
|
10
10
|
id_counts = {}
|
11
11
|
responses.each do |r|
|
12
12
|
if r['target'].is_a?(Array)
|
@@ -33,17 +33,17 @@ module RenderTurboStream
|
|
33
33
|
# Helper for the developer for writing tests: Array with all attributes of all turbo-stream and turbo-channel actions that runned on the last request
|
34
34
|
|
35
35
|
def all_turbo_responses
|
36
|
-
RenderTurboStream::Test::Request::Libs.
|
36
|
+
RenderTurboStream::Test::Request::Libs.new(response).all_turbo_responses
|
37
37
|
end
|
38
38
|
|
39
39
|
# Returns Array of all target_ids that arre affected at least once on the last response
|
40
40
|
def turbo_targets
|
41
|
-
RenderTurboStream::Test::Request::Libs.
|
41
|
+
RenderTurboStream::Test::Request::Libs.new(response).turbo_targets
|
42
42
|
end
|
43
43
|
|
44
44
|
# Returns the path sent to turbo_stream.redirect_to.
|
45
|
-
def
|
46
|
-
resps = RenderTurboStream::Test::Request::Libs.
|
45
|
+
def assert_turbo_redirect_to(path)
|
46
|
+
resps = RenderTurboStream::Test::Request::Libs.new(response).all_turbo_responses
|
47
47
|
url = nil
|
48
48
|
resps.each do |r|
|
49
49
|
if r['type'] == 'stream-command'
|
@@ -56,19 +56,31 @@ module RenderTurboStream
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
59
|
-
url
|
59
|
+
assert url == path, "Expected turbo_redirect_to «#{path}» but got: «#{url}»"
|
60
60
|
end
|
61
61
|
|
62
62
|
# assert one or more specific actions to a target_id
|
63
|
-
def assert_stream_response(target_id, action: nil, count: 1,
|
63
|
+
def assert_stream_response(target_id, action: nil, count: 1, &block)
|
64
64
|
|
65
|
-
libs = RenderTurboStream::Test::Request::Libs
|
65
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
66
66
|
|
67
|
-
r = libs.select_responses(
|
67
|
+
r = libs.select_responses( false, RenderTurboStream::Libs.target_id_to_target(target_id), action, &block)
|
68
68
|
|
69
69
|
assert(
|
70
70
|
r[:responses].length == count,
|
71
|
-
libs.assert_error_message(count, r[:responses].length, r[:log])
|
71
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def assert_stream_action(action, target_id: nil, count: 1, &block)
|
76
|
+
|
77
|
+
libs = RenderTurboStream::Test::Request::Libs.new(response)
|
78
|
+
|
79
|
+
r = libs.select_actions( false, action, &block)
|
80
|
+
|
81
|
+
assert(
|
82
|
+
r[:responses].length == count,
|
83
|
+
libs.class.assert_error_message(count, r[:responses].length, r[:log])
|
72
84
|
)
|
73
85
|
end
|
74
86
|
|
@@ -2,10 +2,15 @@ module RenderTurboStream
|
|
2
2
|
module Test
|
3
3
|
module Request
|
4
4
|
class Libs
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@response = response
|
8
|
+
end
|
9
|
+
|
10
|
+
def all_turbo_responses
|
11
|
+
e = Nokogiri::HTML(@response.body).search('#rendered-partials').first
|
7
12
|
res = (e.present? ? JSON.parse(e.inner_html) : [])
|
8
|
-
response.headers.each do |k, v|
|
13
|
+
@response.headers.each do |k, v|
|
9
14
|
next unless k.match(/^test-turbo-channel-[\d]+$/)
|
10
15
|
h = JSON.parse(v)
|
11
16
|
res.push(h)
|
@@ -13,90 +18,119 @@ module RenderTurboStream
|
|
13
18
|
res
|
14
19
|
end
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
# checks only for rendered partials, not for actions
|
22
|
+
|
23
|
+
def select_responses(channel, target, action, prohibit_multiple_to_same_target: [:replace], &block)
|
24
|
+
all = all_turbo_responses
|
18
25
|
res = { log: [], responses: [] }
|
26
|
+
raise 'Missing argument: target' unless target.present?
|
19
27
|
|
20
|
-
if !
|
21
|
-
res[:log].push("
|
28
|
+
if !target.present?
|
29
|
+
res[:log].push("missing argument: target")
|
22
30
|
elsif prohibit_multiple_to_same_target.present?
|
23
31
|
has_prohibited_action = false
|
24
32
|
c = all.select do |e|
|
25
33
|
if prohibit_multiple_to_same_target.include?(e['action']&.to_sym)
|
26
34
|
has_prohibited_action = true
|
27
35
|
end
|
28
|
-
e['target'] ==
|
36
|
+
e['target'] == target
|
29
37
|
end.length
|
30
38
|
if c >= 2 && has_prohibited_action
|
31
|
-
res[:log].push("Target
|
39
|
+
res[:log].push("Target «#{target}» is targeted #{c} #{'time'.pluralize(c)} which is prohibited for the actions #{prohibit_multiple_to_same_target.join(', ')}")
|
32
40
|
return res
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
36
|
-
|
44
|
+
target_matched = false
|
45
|
+
permitted_types = if channel == false
|
46
|
+
['stream-partial', 'stream-template']
|
47
|
+
else
|
48
|
+
['stream-partial', 'stream-template', 'channel-partial', 'channel-template']
|
49
|
+
end
|
37
50
|
|
38
51
|
responses = all.select do |a|
|
39
|
-
types = [a['type']]
|
40
|
-
if ['channel-partial', 'channel-template'].include?(a['type'])
|
41
|
-
types.push('channel')
|
42
|
-
elsif ['stream-partial'].include?(a['type'])
|
43
|
-
types.push('stream')
|
44
|
-
elsif ['stream-command'].include?(a['type'])
|
45
|
-
types.push('command')
|
46
|
-
types.push('stream')
|
47
|
-
elsif ['channel-command'].include?(a['type'])
|
48
|
-
types.push('command')
|
49
|
-
types.push('channel')
|
50
|
-
end
|
51
52
|
|
52
|
-
if
|
53
|
-
|
53
|
+
if a['target'] == target
|
54
|
+
target_matched = true
|
54
55
|
end
|
55
56
|
|
56
|
-
if
|
57
|
+
if a['target'] != target
|
57
58
|
false
|
58
|
-
elsif
|
59
|
-
res[:log].push("
|
59
|
+
elsif !permitted_types.include?(a['type'])
|
60
|
+
res[:log].push("Type «#{a['type']}» does not match the types you are looking for: #{permitted_types}")
|
60
61
|
false
|
61
62
|
elsif channel.present? && a['channel'] != channel.to_s
|
62
|
-
res[:log].push("Target
|
63
|
+
res[:log].push("Target «#{target}»: Channel #{a['channel']} not matching with given channel «#{channel}»")
|
63
64
|
false
|
64
65
|
elsif action.present? && a['action'] != action.to_s
|
65
|
-
res[:log].push("Target
|
66
|
+
res[:log].push("Target «#{target}»: Action «#{a['action']}» not matching with given action «#{action}»")
|
66
67
|
false
|
67
68
|
elsif block_given?
|
68
|
-
|
69
|
-
|
70
|
-
if yield(args)
|
69
|
+
nok = Nokogiri::HTML(a['html_response'])
|
70
|
+
if yield(nok).present?
|
71
71
|
true
|
72
72
|
else
|
73
|
-
res[:log].push("Given block not matching
|
73
|
+
res[:log].push("Target «#{target}»: Given block not matching in => «#{nok.inner_html}»")
|
74
74
|
false
|
75
75
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
else
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
unless target_matched
|
82
|
+
res[:log].push("Target «#{target}» not found in targets: #{turbo_targets}")
|
83
|
+
end
|
84
|
+
|
85
|
+
res[:responses] = responses
|
86
|
+
res
|
87
|
+
end
|
88
|
+
|
89
|
+
# checks only for commands, not for rendered partials
|
90
|
+
def select_actions(channel, action, &block)
|
91
|
+
all = all_turbo_responses
|
92
|
+
res = { log: [], responses: [] }
|
93
|
+
possible_types = ['stream-command', 'channel-command']
|
94
|
+
raise 'Missing argument: action' unless action.present?
|
95
|
+
|
96
|
+
action_found = false
|
97
|
+
|
98
|
+
responses = all.select do |a|
|
99
|
+
if a['action'] == action.to_s
|
100
|
+
action_found = true
|
101
|
+
end
|
102
|
+
|
103
|
+
if a['action'] != action.to_s
|
104
|
+
false
|
105
|
+
elsif !possible_types.include?(a['type'])
|
106
|
+
res[:log].push("Action «#{action}» is type «#{a['type']}» (accepted types are: #{possible_types.join(', ')})")
|
107
|
+
false
|
108
|
+
elsif channel.present? && a['channel'] != channel.to_s
|
109
|
+
res[:log].push("Channel «#{channel}» not found within action «#{action}»")
|
110
|
+
false
|
111
|
+
elsif block_given?
|
112
|
+
args = a['array'][1..-1]
|
113
|
+
if yield(args)
|
79
114
|
true
|
80
115
|
else
|
81
|
-
res[:log].push("
|
116
|
+
res[:log].push("Given block not matching Arguments => «#{args}»")
|
82
117
|
false
|
83
118
|
end
|
84
|
-
end
|
85
119
|
else
|
86
120
|
true
|
87
121
|
end
|
88
122
|
end
|
89
123
|
|
90
|
-
unless
|
91
|
-
res[:log].push("
|
124
|
+
unless action_found
|
125
|
+
res[:log].push("Action «#{action}» not found under responded actions: #{turbo_actions}")
|
92
126
|
end
|
93
127
|
|
94
128
|
res[:responses] = responses
|
95
129
|
res
|
96
130
|
end
|
97
131
|
|
98
|
-
def
|
99
|
-
responses = all_turbo_responses
|
132
|
+
def turbo_targets
|
133
|
+
responses = all_turbo_responses
|
100
134
|
targets = []
|
101
135
|
responses.each do |r|
|
102
136
|
if r['target'].is_a?(Array)
|
@@ -111,6 +145,27 @@ module RenderTurboStream
|
|
111
145
|
targets
|
112
146
|
end
|
113
147
|
|
148
|
+
def turbo_actions
|
149
|
+
responses = all_turbo_responses
|
150
|
+
actions = []
|
151
|
+
responses.each do |r|
|
152
|
+
actions.push(r['action']) unless actions.include?(r['action'])
|
153
|
+
end
|
154
|
+
|
155
|
+
actions
|
156
|
+
end
|
157
|
+
|
158
|
+
def targets_counts
|
159
|
+
responses = all_turbo_responses
|
160
|
+
targets = {}
|
161
|
+
responses.each do |r|
|
162
|
+
targets[r['target']] ||= 0
|
163
|
+
targets[r['target']] += 1
|
164
|
+
end
|
165
|
+
|
166
|
+
targets
|
167
|
+
end
|
168
|
+
|
114
169
|
def self.assert_error_message(expected, received, log)
|
115
170
|
[
|
116
171
|
"Expected #{expected} #{'response'.pluralize(expected)} but found #{received}.",
|
data/lib/render_turbo_stream.rb
CHANGED
@@ -12,4 +12,7 @@ require 'render_turbo_stream/controller_channel_helpers'
|
|
12
12
|
require 'render_turbo_stream/channel_view_helpers'
|
13
13
|
|
14
14
|
require 'render_turbo_stream/channel_libs'
|
15
|
+
require 'render_turbo_stream/controller_libs'
|
16
|
+
require 'render_turbo_stream/check_template'
|
17
|
+
require 'render_turbo_stream/libs'
|
15
18
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: render_turbo_stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- christian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,11 +24,12 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 7.0.3
|
27
|
-
description: Handles translated flash messages, sets status
|
28
|
-
Turbo Stream or Turbo::StreamsChannel directly
|
29
|
-
*.turbo_stream.* templates. Together with
|
30
|
-
actions, it can run javascript. Redirects
|
31
|
-
ways. Through request testing helpers, this
|
27
|
+
description: Handles translated flash messages, sets status, wrapps partials in the
|
28
|
+
necessary frames and renders via Turbo Stream or Turbo::StreamsChannel directly
|
29
|
+
from the controller. No need to write *.turbo_stream.* templates. Together with
|
30
|
+
the turbo_power gem or custom turbo_stream actions, it can run javascript. Redirects
|
31
|
+
can be handled easily and in multiple ways. Through request testing helpers, this
|
32
|
+
allows for consistent testing strategy.
|
32
33
|
email:
|
33
34
|
- christian@sedlmair.ch
|
34
35
|
executables: []
|
@@ -39,6 +40,7 @@ files:
|
|
39
40
|
- Rakefile
|
40
41
|
- app/controllers/render_turbo_stream/application_controller.rb
|
41
42
|
- app/controllers/render_turbo_stream/render_turbo_stream_render_controller.rb
|
43
|
+
- app/views/layouts/_add_turbo_frame_tag.html.erb
|
42
44
|
- app/views/render_turbo_stream.turbo_stream.erb
|
43
45
|
- app/views/render_turbo_stream_command.html.erb
|
44
46
|
- app/views/render_turbo_stream_empty_template.html.erb
|
@@ -46,9 +48,12 @@ files:
|
|
46
48
|
- lib/render_turbo_stream.rb
|
47
49
|
- lib/render_turbo_stream/channel_libs.rb
|
48
50
|
- lib/render_turbo_stream/channel_view_helpers.rb
|
51
|
+
- lib/render_turbo_stream/check_template.rb
|
49
52
|
- lib/render_turbo_stream/controller_channel_helpers.rb
|
50
53
|
- lib/render_turbo_stream/controller_helpers.rb
|
54
|
+
- lib/render_turbo_stream/controller_libs.rb
|
51
55
|
- lib/render_turbo_stream/engine.rb
|
56
|
+
- lib/render_turbo_stream/libs.rb
|
52
57
|
- lib/render_turbo_stream/railtie.rb
|
53
58
|
- lib/render_turbo_stream/test/request/channel_helpers.rb
|
54
59
|
- lib/render_turbo_stream/test/request/helpers.rb
|
@@ -80,6 +85,5 @@ requirements: []
|
|
80
85
|
rubygems_version: 3.4.12
|
81
86
|
signing_key:
|
82
87
|
specification_version: 4
|
83
|
-
summary: Complete workflow for turbo
|
84
|
-
Rails like.
|
88
|
+
summary: Complete workflow for turbo that just works and has a testing strategy.
|
85
89
|
test_files: []
|