render_turbo_stream 2.1.2 → 3.0.1
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 +40 -73
- data/app/views/render_turbo_stream_command.html.erb +1 -0
- data/app/views/render_turbo_stream_empty_template.html.erb +0 -0
- data/app/views/{render_turbo_stream.html.erb → render_turbo_stream_request_test.html.erb} +3 -3
- data/lib/render_turbo_stream/channel_libs.rb +94 -0
- data/lib/render_turbo_stream/channel_view_helpers.rb +36 -0
- data/lib/render_turbo_stream/controller_channel_helpers.rb +121 -0
- data/lib/render_turbo_stream/controller_helpers.rb +241 -0
- data/lib/render_turbo_stream/test/request/channel_helpers.rb +55 -0
- data/lib/render_turbo_stream/test/request/helpers.rb +78 -0
- data/lib/render_turbo_stream/test/request/libs.rb +156 -0
- data/lib/render_turbo_stream/version.rb +1 -1
- data/lib/render_turbo_stream.rb +7 -327
- metadata +14 -12
- data/app/models/render_turbo_stream/cable_stream.rb +0 -4
- data/db/migrate/20230428115511_cable_streams.rb +0 -4
- data/db/migrate/20230428115724_create_render_turbo_stream_cable_streams.rb +0 -11
- data/lib/render_turbo_stream/test/request_helpers.rb +0 -100
- data/lib/render_turbo_stream/test/rspec_request_helpers.rb +0 -34
- data/lib/render_turbo_stream/turbo_cable_helpers.rb +0 -75
- data/lib/render_turbo_stream/turbo_cable_view_helpers.rb +0 -36
@@ -0,0 +1,241 @@
|
|
1
|
+
module RenderTurboStream
|
2
|
+
module ControllerHelpers
|
3
|
+
|
4
|
+
# Handles translated flash messages as defined in translations and configs.
|
5
|
+
# If :redirect_on_success_to and channel set up and use_channel_for_turbo_stream_save are configured, sends flash message by channel_to_me.
|
6
|
+
# you can add more stream actions to the same response
|
7
|
+
|
8
|
+
def turbo_stream_save(
|
9
|
+
save_action,
|
10
|
+
redirect_on_success_to: nil, # does a regular redirect. Works if you are inside a turbo_frame and just want to redirect inside that frame BUT CANNOT STREAM OTHERS ACTIONS ON THE SAME RESPONSE https://github.com/rails/rails/issues/48056
|
11
|
+
turbo_redirect_on_success_to: nil, # does a full page redirect (break out of all frames by turbo_power redirect)
|
12
|
+
object: nil, # object used in save_action, example: @customer
|
13
|
+
id: nil, # if nil: no partial is rendered
|
14
|
+
partial: nil, # example: 'customers/form' default: "#{controller_path}/#{id}"
|
15
|
+
action: 'replace', # options: append, prepend
|
16
|
+
flash_action: action_name, # options: 'update', 'create', otherwise you have to declare a translation in config/locales like "activerecord.success.#{flash_action}" and "activerecord.errors.#{flash_action}"
|
17
|
+
locals: {}, # locals used by the partial
|
18
|
+
streams_on_success: [
|
19
|
+
{
|
20
|
+
id: nil,
|
21
|
+
partial: 'form',
|
22
|
+
locals: {},
|
23
|
+
action: 'replace'
|
24
|
+
}
|
25
|
+
], # additional partials that should be rendered if save_action succeeded
|
26
|
+
streams_on_error: [
|
27
|
+
{
|
28
|
+
id: nil,
|
29
|
+
partial: 'form',
|
30
|
+
locals: {},
|
31
|
+
action: 'replace'
|
32
|
+
}
|
33
|
+
], # additional partials that should be rendered if save_action failed
|
34
|
+
add_flash_alerts: [], #=> array of strings
|
35
|
+
add_flash_notices: [], #=> array of strings
|
36
|
+
flashes_on_success: [], #=> array of strings
|
37
|
+
flashes_on_error: [] #=> array of strings
|
38
|
+
)
|
39
|
+
|
40
|
+
unless object
|
41
|
+
object = eval("@#{controller_name.classify.underscore}")
|
42
|
+
end
|
43
|
+
|
44
|
+
#== Streams / Partials
|
45
|
+
|
46
|
+
streams = (id ? [id: id, partial: partial, locals: locals, action: action] : [])
|
47
|
+
|
48
|
+
if save_action
|
49
|
+
response.status = 200
|
50
|
+
streams_on_success.each do |s|
|
51
|
+
if s.is_a?(Array)
|
52
|
+
streams.push(s)
|
53
|
+
elsif s.is_a?(Hash) && s[:id].present?
|
54
|
+
streams.push(s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
else
|
58
|
+
response.status = :unprocessable_entity
|
59
|
+
streams += streams_on_error.select { |s| s[:id].present? }
|
60
|
+
end
|
61
|
+
|
62
|
+
#== FLASHES
|
63
|
+
|
64
|
+
model_name = object.model_name.human
|
65
|
+
if save_action
|
66
|
+
flash_notices = if flashes_on_success.present?
|
67
|
+
flashes_on_success
|
68
|
+
elsif flash_action.to_s == 'create'
|
69
|
+
str = I18n.t(
|
70
|
+
'activerecord.success.successfully_created',
|
71
|
+
default: '%<model_name>s successfully created'
|
72
|
+
)
|
73
|
+
[format(str, model_name: model_name)]
|
74
|
+
elsif flash_action.to_s == 'update'
|
75
|
+
str = I18n.t(
|
76
|
+
'activerecord.success.successfully_updated',
|
77
|
+
default: '%<model_name>s successfully updated'
|
78
|
+
)
|
79
|
+
[format(str, model_name: model_name)]
|
80
|
+
else
|
81
|
+
str = I18n.t(
|
82
|
+
"activerecord.success.#{flash_action}",
|
83
|
+
default: '%<model_name>s successfully updated'
|
84
|
+
)
|
85
|
+
[format(str, model_name: model_name)]
|
86
|
+
end
|
87
|
+
flash_alerts = []
|
88
|
+
else
|
89
|
+
flash_alerts = if flashes_on_error.present?
|
90
|
+
flashes_on_error
|
91
|
+
elsif flash_action.to_s == 'create'
|
92
|
+
str = I18n.t(
|
93
|
+
'activerecord.errors.messages.could_not_create',
|
94
|
+
default: '%<model_name>s could not be created'
|
95
|
+
)
|
96
|
+
[format(str, model_name: model_name)]
|
97
|
+
elsif flash_action.to_s == 'update'
|
98
|
+
str = I18n.t(
|
99
|
+
'activerecord.errors.messages.could_not_update',
|
100
|
+
default: '%<model_name>s could not be updated'
|
101
|
+
)
|
102
|
+
[format(str, model_name: model_name)]
|
103
|
+
else
|
104
|
+
str = I18n.t(
|
105
|
+
"activerecord.errors.messages.#{flash_action}",
|
106
|
+
default: '%<model_name>s could not be updated'
|
107
|
+
)
|
108
|
+
[format(str, model_name: model_name)]
|
109
|
+
end
|
110
|
+
flash_notices = []
|
111
|
+
end
|
112
|
+
|
113
|
+
flash_notices += add_flash_notices.to_a
|
114
|
+
flash_alerts += add_flash_alerts.to_a
|
115
|
+
_flash_id = Rails.configuration.x.render_turbo_stream.flash_id
|
116
|
+
flash_id = (_flash_id ? _flash_id : "ERROR, MISSING CONFIG => config.x.render_turbo_stream.flash_id")
|
117
|
+
flash_partial = Rails.configuration.x.render_turbo_stream.flash_partial
|
118
|
+
flash_action = Rails.configuration.x.render_turbo_stream.flash_action
|
119
|
+
flash_notices.each do |notice|
|
120
|
+
next unless notice.present?
|
121
|
+
# inside the flash partial has to be a loop that handles all theese flashes
|
122
|
+
flash_stream = {
|
123
|
+
id: flash_id,
|
124
|
+
partial: flash_partial,
|
125
|
+
action: flash_action,
|
126
|
+
locals: { success: true, message: notice }
|
127
|
+
}
|
128
|
+
streams.push(flash_stream)
|
129
|
+
end
|
130
|
+
flash_alerts.each do |alert|
|
131
|
+
next unless alert.present?
|
132
|
+
# inside the flash partial has to be a loop that handles all theese flashes
|
133
|
+
flash_stream = {
|
134
|
+
id: flash_id,
|
135
|
+
partial: flash_partial,
|
136
|
+
action: flash_action,
|
137
|
+
locals: { success: false, message: alert }
|
138
|
+
}
|
139
|
+
streams.push(flash_stream)
|
140
|
+
end
|
141
|
+
|
142
|
+
#== render
|
143
|
+
|
144
|
+
if save_action && turbo_redirect_on_success_to.present?
|
145
|
+
response.status = 302
|
146
|
+
flash[:alert] = flash_alerts
|
147
|
+
flash[:notice] = flash_notices
|
148
|
+
Rails.logger.debug(" • Set flash[:alert] => #{flash_alerts}") if flash_alerts.present?
|
149
|
+
Rails.logger.debug(" • Set flash[:notice] => #{flash_notices}") if flash_notices.present?
|
150
|
+
render_turbo_stream([
|
151
|
+
[
|
152
|
+
:redirect_to,
|
153
|
+
turbo_redirect_on_success_to
|
154
|
+
]
|
155
|
+
])
|
156
|
+
elsif save_action && redirect_on_success_to.present?
|
157
|
+
response.status = 302
|
158
|
+
if Rails.configuration.x.render_turbo_stream.use_channel_for_turbo_stream_save && helpers.user_signed_in?
|
159
|
+
streams.each do |s|
|
160
|
+
next unless s.is_a?(Hash)
|
161
|
+
Rails.logger.debug(" • Send by Cable => «#{s}»")
|
162
|
+
render_to_me(
|
163
|
+
s[:id],
|
164
|
+
flash_action,
|
165
|
+
partial: s[:partial],
|
166
|
+
locals: s[:locals]
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
else
|
171
|
+
flash[:alert] = flash_alerts
|
172
|
+
flash[:notice] = flash_notices
|
173
|
+
Rails.logger.debug(" • Set flash[:alert] => #{flash_alerts}") if flash_alerts.present?
|
174
|
+
Rails.logger.debug(" • Set flash[:notice] => #{flash_notices}") if flash_notices.present?
|
175
|
+
end
|
176
|
+
redirect_to redirect_on_success_to
|
177
|
+
|
178
|
+
else
|
179
|
+
flash.now[:alert] = flash_alerts
|
180
|
+
flash.now[:notice] = flash_notices
|
181
|
+
render_turbo_stream(streams)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# renders a array of partials to send by turbo-stream and / or actions like turbo_power gem includes, to turbo_stream
|
187
|
+
def render_turbo_stream(array)
|
188
|
+
|
189
|
+
ary = []
|
190
|
+
array.each do |pr|
|
191
|
+
if !pr.present?
|
192
|
+
Rails.logger.warn " WARNING render_turbo_stream: Empty element inside attributes: «#{array}»"
|
193
|
+
elsif pr.is_a?(Hash)
|
194
|
+
props = pr.symbolize_keys
|
195
|
+
raise "missing attribute :id in #{props}" if !props[:id].present?
|
196
|
+
part = (props[:partial].present? ? props[:partial] : props[:id]).gsub('-', '_')
|
197
|
+
partial = (part.to_s.include?('/') ? part : [controller_path, part].join('/'))
|
198
|
+
r = props
|
199
|
+
r[:action] = (props[:action].present? ? props[:action] : :replace)
|
200
|
+
r[:partial] = partial
|
201
|
+
r[:type] = 'stream-partial'
|
202
|
+
ary.push(r)
|
203
|
+
elsif pr.is_a?(Array)
|
204
|
+
raise "array has to contain at least one element: #{pr}" unless pr.first.present?
|
205
|
+
ary.push(pr)
|
206
|
+
else
|
207
|
+
raise "ERROR render_turbo_stream invalid type: Only hash or array allowed"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if request.format.to_sym == :turbo_stream
|
212
|
+
render template: 'render_turbo_stream', locals: { streams: ary }, layout: false, formats: :turbo_stream
|
213
|
+
else
|
214
|
+
Rails.logger.debug(" • Render Turbo Stream RENDERING AS HTML because request.format => #{request.format}")
|
215
|
+
render template: 'render_turbo_stream_request_test', locals: { streams: ary }, layout: false, formats: :html
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
# renders a partial to turbo_stream
|
221
|
+
|
222
|
+
def stream_partial(
|
223
|
+
id,
|
224
|
+
partial: nil, #=> default: id
|
225
|
+
action: :replace,
|
226
|
+
locals: {}
|
227
|
+
)
|
228
|
+
render_turbo_stream(
|
229
|
+
[
|
230
|
+
{
|
231
|
+
id: id,
|
232
|
+
partial: partial,
|
233
|
+
action: action,
|
234
|
+
locals: locals
|
235
|
+
}
|
236
|
+
]
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RenderTurboStream
|
2
|
+
module Test
|
3
|
+
module Request
|
4
|
+
module ChannelHelpers
|
5
|
+
|
6
|
+
# Assert a action by turbo streams channel to the current_user
|
7
|
+
|
8
|
+
def assert_channel_to_me(user, target_id, action: nil, count: 1, &block)
|
9
|
+
|
10
|
+
channel = "authenticated-user-#{user.id}"
|
11
|
+
|
12
|
+
libs = RenderTurboStream::Test::Request::Libs
|
13
|
+
|
14
|
+
r = libs.select_responses(response, channel, target_id, action, type: :channel, &block)
|
15
|
+
|
16
|
+
assert(
|
17
|
+
r[:responses].length == count,
|
18
|
+
libs.assert_error_message(count, r[:responses].length, r[:log])
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Assert a action by turbo streams channel to a group of authenticated users
|
23
|
+
|
24
|
+
def assert_channel_to_authenticated_group(group, target_id, action: nil, count: 1, &block)
|
25
|
+
|
26
|
+
channel = "authenticated-group-#{group}"
|
27
|
+
|
28
|
+
libs = RenderTurboStream::Test::Request::Libs
|
29
|
+
|
30
|
+
r = libs.select_responses(response, channel, target_id, action, type: :channel, &block)
|
31
|
+
|
32
|
+
assert(
|
33
|
+
r[:responses].length == count,
|
34
|
+
libs.assert_error_message(count, r[:responses].length, r[:log])
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Assert a action by turbo streams channel to all visitors of the page
|
39
|
+
|
40
|
+
def assert_channel_to_all(target_id, action: nil, count: 1, &block)
|
41
|
+
|
42
|
+
libs = RenderTurboStream::Test::Request::Libs
|
43
|
+
|
44
|
+
r = libs.select_responses(response, 'all', target_id, action, type: :channel, &block)
|
45
|
+
|
46
|
+
assert(
|
47
|
+
r[:responses].length == count,
|
48
|
+
libs.assert_error_message(count, r[:responses].length, r[:log])
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RenderTurboStream
|
2
|
+
module Test
|
3
|
+
module Request
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
# Assert that each of given target_ids is targeted exactly once
|
7
|
+
|
8
|
+
def assert_once_targeted(*target_ids)
|
9
|
+
responses = RenderTurboStream::Test::Request::Libs.all_turbo_responses(response)
|
10
|
+
id_counts = {}
|
11
|
+
responses.each do |r|
|
12
|
+
if r['target'].is_a?(Array)
|
13
|
+
r['target'].each do |t|
|
14
|
+
id = (t[0] == '#' ? t[1..-1] : t)
|
15
|
+
id_counts[t.to_s] ||= 0
|
16
|
+
id_counts[t.to_s] += 1
|
17
|
+
end
|
18
|
+
else
|
19
|
+
id = (r['target'][0] == '#' ? r['target'][1..-1] : r['target'])
|
20
|
+
id_counts[id] ||= 0
|
21
|
+
id_counts[id] += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
assert(id_counts.keys.length == target_ids.length, "You checked for #{target_ids.length} but #{id_counts.keys.length} targeted: #{id_counts.keys.join(', ')}")
|
26
|
+
target_ids.each do |id|
|
27
|
+
assert(id_counts.key?(id), "The id #{id} is not within the targeted ids: #{id_counts.keys.join(', ')}")
|
28
|
+
expect(id_counts[id]).to eq(1)
|
29
|
+
assert(id_counts[id] == 1, "The id #{id} is targeted #{id_counts[id]} #{'time'.pluralize(id_counts[id])}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
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
|
+
|
35
|
+
def all_turbo_responses
|
36
|
+
RenderTurboStream::Test::Request::Libs.all_turbo_responses(response)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns Array of all target_ids that arre affected at least once on the last response
|
40
|
+
def turbo_targets
|
41
|
+
RenderTurboStream::Test::Request::Libs.turbo_targets(response)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the path sent to turbo_stream.redirect_to.
|
45
|
+
def turbo_redirect_to
|
46
|
+
resps = RenderTurboStream::Test::Request::Libs.all_turbo_responses(response)
|
47
|
+
url = nil
|
48
|
+
resps.each do |r|
|
49
|
+
if r['type'] == 'stream-command'
|
50
|
+
if r['array'].first == 'redirect_to'
|
51
|
+
if url
|
52
|
+
url = 'ERROR: REDIRECT CALLED MORE THAN ONCE'
|
53
|
+
else
|
54
|
+
url = r['array'].second
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
url
|
60
|
+
end
|
61
|
+
|
62
|
+
# assert one or more specific actions to a target_id
|
63
|
+
def assert_stream_response(target_id, action: nil, count: 1, type: :stream, &block)
|
64
|
+
|
65
|
+
libs = RenderTurboStream::Test::Request::Libs
|
66
|
+
|
67
|
+
r = libs.select_responses(response, false, target_id, action, type: type, &block)
|
68
|
+
|
69
|
+
assert(
|
70
|
+
r[:responses].length == count,
|
71
|
+
libs.assert_error_message(count, r[:responses].length, r[:log])
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module RenderTurboStream
|
2
|
+
module Test
|
3
|
+
module Request
|
4
|
+
class Libs
|
5
|
+
def self.all_turbo_responses(response)
|
6
|
+
e = Nokogiri::HTML(response.body).search('#rendered-partials').first
|
7
|
+
res = (e.present? ? JSON.parse(e.inner_html) : [])
|
8
|
+
response.headers.each do |k, v|
|
9
|
+
next unless k.match(/^test-turbo-channel-[\d]+$/)
|
10
|
+
h = JSON.parse(v)
|
11
|
+
res.push(h)
|
12
|
+
end
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.select_responses(response, channel, target_id, action, type: nil, prohibit_multiple_to_same_target: [:replace], &block)
|
17
|
+
all = all_turbo_responses(response)
|
18
|
+
res = { log: [], responses: [] }
|
19
|
+
|
20
|
+
if !target_id.present?
|
21
|
+
res[:log].push("No target «##{target_id}» provided!")
|
22
|
+
elsif prohibit_multiple_to_same_target.present?
|
23
|
+
has_prohibited_action = false
|
24
|
+
c = all.select do |e|
|
25
|
+
if prohibit_multiple_to_same_target.include?(e['action']&.to_sym)
|
26
|
+
has_prohibited_action = true
|
27
|
+
end
|
28
|
+
e['target'] == "##{target_id}"
|
29
|
+
end.length
|
30
|
+
if c >= 2 && has_prohibited_action
|
31
|
+
res[:log].push("Target «##{target_id}» is targeted #{c} #{'time'.pluralize(c)} which is prohibited for the actions #{prohibit_multiple_to_same_target.join(', ')}")
|
32
|
+
return res
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
id_matched = false
|
37
|
+
|
38
|
+
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
|
+
if target_id.present? && a['target'] == "##{target_id}"
|
53
|
+
id_matched = true
|
54
|
+
end
|
55
|
+
|
56
|
+
if target_id.present? && a['target'] != "##{target_id}"
|
57
|
+
false
|
58
|
+
elsif type.present? && !types.include?(type.to_s)
|
59
|
+
res[:log].push("Target «##{target_id}»: Types #{types.join(', ')} not matching with given type «#{type}»")
|
60
|
+
false
|
61
|
+
elsif channel.present? && a['channel'] != channel.to_s
|
62
|
+
res[:log].push("Target «##{target_id}»: Channel #{a['channel']} not matching with given channel «#{channel}»")
|
63
|
+
false
|
64
|
+
elsif action.present? && a['action'] != action.to_s
|
65
|
+
res[:log].push("Target «##{target_id}»: Action #{a['action']} not matching with given action «#{action}»")
|
66
|
+
false
|
67
|
+
elsif block_given?
|
68
|
+
if types.include?('command')
|
69
|
+
args = a['array'][1..-1]
|
70
|
+
if yield(args)
|
71
|
+
true
|
72
|
+
else
|
73
|
+
res[:log].push("Given block not matching Arguments => «#{args}»")
|
74
|
+
false
|
75
|
+
end
|
76
|
+
else
|
77
|
+
nok = Nokogiri::HTML(a['html_response'])
|
78
|
+
if yield(nok).present?
|
79
|
+
true
|
80
|
+
else
|
81
|
+
res[:log].push("Target «##{target_id}»: Given block not matching (checked by .present?)")
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
else
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
unless id_matched
|
91
|
+
res[:log].push("Target «##{target_id}» not found")
|
92
|
+
end
|
93
|
+
|
94
|
+
res[:responses] = responses
|
95
|
+
res
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.turbo_targets(response)
|
99
|
+
responses = all_turbo_responses(response)
|
100
|
+
targets = []
|
101
|
+
responses.each do |r|
|
102
|
+
if r['target'].is_a?(Array)
|
103
|
+
r['target'].each do |t|
|
104
|
+
targets.push(t) unless targets.include?(t)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
targets.push(r['target']) unless targets.include?(r['target'])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
targets
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.assert_error_message(expected, received, log)
|
115
|
+
[
|
116
|
+
"Expected #{expected} #{'response'.pluralize(expected)} but found #{received}.",
|
117
|
+
(log.present? ? "Messages => «#{log.join(', ')}»" : nil)
|
118
|
+
].compact.join(' ')
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.first_arg_is_html_id(method)
|
122
|
+
config = Rails.configuration.x.render_turbo_stream.first_argument_is_html_id
|
123
|
+
default = [
|
124
|
+
:graft,
|
125
|
+
:morph,
|
126
|
+
:inner_html,
|
127
|
+
:insert_adjacent_text,
|
128
|
+
:outer_html,
|
129
|
+
:text_content,
|
130
|
+
:add_css_class,
|
131
|
+
:remove_attribute,
|
132
|
+
:remove_css_class,
|
133
|
+
:set_attribute,
|
134
|
+
:set_dataset_attribute,
|
135
|
+
:set_property,
|
136
|
+
:set_style,
|
137
|
+
:set_styles,
|
138
|
+
:set_value,
|
139
|
+
:dispatch_event,
|
140
|
+
:reset_form,
|
141
|
+
:clear_storage,
|
142
|
+
:scroll_into_view,
|
143
|
+
:set_focus,
|
144
|
+
:turbo_frame_reload,
|
145
|
+
:turbo_frame_set_src,
|
146
|
+
:replace,
|
147
|
+
:append,
|
148
|
+
:prepend
|
149
|
+
]
|
150
|
+
(config.present? ? config : default).map { |m| m.to_sym }.include?(method.to_sym)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|