render_turbo_stream 3.0.5 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|