cable_ready 4.4.6 → 5.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +228 -161
  3. data/Gemfile.lock +146 -99
  4. data/LATEST +1 -0
  5. data/README.md +13 -13
  6. data/Rakefile +8 -2
  7. data/app/channels/cable_ready/stream.rb +12 -0
  8. data/app/helpers/cable_ready_helper.rb +11 -0
  9. data/app/jobs/cable_ready_broadcast_job.rb +14 -0
  10. data/bin/standardize +1 -1
  11. data/cable_ready.gemspec +4 -2
  12. data/lib/cable_ready.rb +44 -0
  13. data/lib/cable_ready/broadcaster.rb +3 -4
  14. data/lib/cable_ready/cable_car.rb +17 -0
  15. data/lib/cable_ready/channel.rb +14 -36
  16. data/lib/cable_ready/channels.rb +22 -68
  17. data/lib/cable_ready/compoundable.rb +11 -0
  18. data/lib/cable_ready/config.rb +78 -0
  19. data/lib/cable_ready/identifiable.rb +30 -0
  20. data/lib/cable_ready/operation_builder.rb +83 -0
  21. data/lib/cable_ready/sanity_checker.rb +151 -0
  22. data/lib/cable_ready/stream_identifier.rb +13 -0
  23. data/lib/cable_ready/version.rb +1 -1
  24. data/lib/generators/cable_ready/channel_generator.rb +71 -0
  25. data/lib/generators/cable_ready/initializer_generator.rb +14 -0
  26. data/lib/generators/cable_ready/stream_from_generator.rb +43 -0
  27. data/lib/generators/cable_ready/templates/config/initializers/cable_ready.rb +18 -0
  28. data/package.json +10 -5
  29. data/tags +58 -35
  30. data/test/lib/cable_ready/cable_car_test.rb +50 -0
  31. data/test/lib/cable_ready/identifiable_test.rb +75 -0
  32. data/test/lib/cable_ready/operation_builder_test.rb +211 -0
  33. data/test/lib/generators/cable_ready/channel_generator_test.rb +157 -0
  34. data/test/support/generator_test_helpers.rb +28 -0
  35. data/test/test_helper.rb +15 -0
  36. data/yarn.lock +134 -124
  37. metadata +66 -11
data/tags CHANGED
@@ -9,49 +9,72 @@ CableReady lib/cable_ready.rb /^module CableReady$/;" m
9
9
  CableReady lib/cable_ready/broadcaster.rb /^module CableReady$/;" m
10
10
  CableReady lib/cable_ready/channel.rb /^module CableReady$/;" m
11
11
  CableReady lib/cable_ready/channels.rb /^module CableReady$/;" m
12
+ CableReady lib/cable_ready/config.rb /^module CableReady$/;" m
12
13
  CableReady lib/cable_ready/version.rb /^module CableReady$/;" m
14
+ CableReady lib/generators/cable_ready/channel_generator.rb /^class CableReady::ChannelGenerator < Rails::Generators::NamedBase$/;" c
15
+ CableReady test/lib/generators/cable_ready/channel_generator_test.rb /^class CableReady::ChannelGeneratorTest < Rails::Generators::TestCase$/;" c
13
16
  Channel lib/cable_ready/channel.rb /^ class Channel$/;" c class:CableReady
14
17
  Channels lib/cable_ready/channels.rb /^ class Channels$/;" c class:CableReady
18
+ ClassMethods test/support/generator_test_helpers.rb /^ module ClassMethods$/;" m class:GeneratorTestHelpers
19
+ Config lib/cable_ready/config.rb /^ class Config$/;" c class:CableReady
15
20
  Engine lib/cable_ready.rb /^ class Engine < Rails::Engine$/;" c class:CableReady
21
+ GeneratorTestHelpers test/support/generator_test_helpers.rb /^module GeneratorTestHelpers$/;" m
16
22
  [] lib/cable_ready/channels.rb /^ def [](identifier)$/;" f class:CableReady.Channels
17
- add_operation lib/cable_ready/channels.rb /^ def add_operation(operation, &implementation)$/;" f class:CableReady.Channels
18
- broadcast lib/cable_ready/channel.rb /^ def broadcast(clear = true)$/;" f class:CableReady.Channel
23
+ add_operation_method lib/cable_ready/channel.rb /^ def add_operation_method(name)$/;" f class:CableReady.Channel
24
+ add_operation_name lib/cable_ready/config.rb /^ def add_operation_name(name)$/;" f class:CableReady.Config
25
+ broadcast lib/cable_ready/channel.rb /^ def broadcast(clear: true)$/;" f class:CableReady.Channel
19
26
  broadcast lib/cable_ready/channels.rb /^ def broadcast(*identifiers, clear: true)$/;" f class:CableReady.Channels
20
- broadcast_to lib/cable_ready/channel.rb /^ def broadcast_to(model, clear = true)$/;" f class:CableReady.Channel
27
+ broadcast_to lib/cable_ready/channel.rb /^ def broadcast_to(model, clear: true)$/;" f class:CableReady.Channel
21
28
  broadcast_to lib/cable_ready/channels.rb /^ def broadcast_to(model, *identifiers, clear: true)$/;" f class:CableReady.Channels
29
+ broadcastable_operations lib/cable_ready/channel.rb /^ def broadcastable_operations$/;" f class:CableReady.Channel
22
30
  cable_ready lib/cable_ready/broadcaster.rb /^ def cable_ready$/;" f class:CableReady.Broadcaster
23
- channel_broadcast lib/cable_ready/channel.rb /^ def channel_broadcast(clear)$/;" f class:CableReady.Channel
24
- channel_broadcast_to lib/cable_ready/channel.rb /^ def channel_broadcast_to(model, clear)$/;" f class:CableReady.Channel
25
- configure lib/cable_ready/channels.rb /^ def self.configure$/;" F class:CableReady.Channels
26
- const.bubbles javascript/cable_ready.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
27
- const.cancelable javascript/cable_ready.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
28
- const.color javascript/cable_ready.js /^ color: true,$/;" p
29
- const.date javascript/cable_ready.js /^ date: true,$/;" p
30
- const.datetime javascript/cable_ready.js /^ datetime: true,$/;" p
31
- const.datetime-local javascript/cable_ready.js /^ 'datetime-local': true,$/;" p
32
- const.detail javascript/cable_ready.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
33
- const.email javascript/cable_ready.js /^ email: true,$/;" p
34
- const.month javascript/cable_ready.js /^ month: true,$/;" p
35
- const.number javascript/cable_ready.js /^ number: true,$/;" p
36
- const.password javascript/cable_ready.js /^ password: true,$/;" p
37
- const.pushState javascript/cable_ready.js /^ pushState: config => {$/;" p
38
- const.range javascript/cable_ready.js /^ range: true,$/;" p
39
- const.search javascript/cable_ready.js /^ search: true,$/;" p
40
- const.select-multiple javascript/cable_ready.js /^ 'select-multiple': true,$/;" p
41
- const.select-one javascript/cable_ready.js /^ 'select-one': true,$/;" p
42
- const.tel javascript/cable_ready.js /^ tel: true,$/;" p
43
- const.text javascript/cable_ready.js /^ text: true,$/;" p
44
- const.textarea javascript/cable_ready.js /^ textarea: true,$/;" p
45
- const.time javascript/cable_ready.js /^ time: true,$/;" p
46
- const.url javascript/cable_ready.js /^ url: true,$/;" p
47
- const.value javascript/cable_ready.js /^ const ignore = { value: true }$/;" p
48
- const.week javascript/cable_ready.js /^ week: true$/;" p
31
+ check_options lib/generators/cable_ready/channel_generator.rb /^ def check_options$/;" f class:CableReady
32
+ config lib/cable_ready.rb /^ def self.config$/;" F class:CableReady
33
+ configure lib/cable_ready.rb /^ def self.configure$/;" F class:CableReady
34
+ const.bubbles javascript/utils.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
35
+ const.cancelable javascript/utils.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
36
+ const.detail javascript/utils.js /^ const init = { bubbles: true, cancelable: true, detail: detail }$/;" p
37
+ const.value javascript/morph_callbacks.js /^ const ignore = { value: true }$/;" p
38
+ create_channel lib/generators/cable_ready/channel_generator.rb /^ def create_channel$/;" f class:CableReady
39
+ create_sample_app test/support/generator_test_helpers.rb /^ def create_sample_app$/;" f class:GeneratorTestHelpers.ClassMethods
40
+ default_operation_names lib/cable_ready/config.rb /^ def default_operation_names$/;" f class:CableReady.Config
49
41
  dom_id lib/cable_ready/broadcaster.rb /^ def dom_id(record, prefix = nil)$/;" f class:CableReady.Broadcaster
50
- enqueue_operation lib/cable_ready/channel.rb /^ def enqueue_operation(key, options)$/;" f class:CableReady.Channel
51
- import.INPUT javascript/cable_ready.js /^ INPUT: true,$/;" p
52
- import.SELECT javascript/cable_ready.js /^ SELECT: true$/;" p
53
- import.TEXTAREA javascript/cable_ready.js /^ TEXTAREA: true,$/;" p
54
- initialize lib/cable_ready/channel.rb /^ def initialize(identifier, available_operations)$/;" f class:CableReady.Channel
42
+ enhance_channels lib/generators/cable_ready/channel_generator.rb /^ def enhance_channels$/;" f class:CableReady
43
+ export.INPUT javascript/enums.js /^ INPUT: true,$/;" p
44
+ export.OPTION javascript/enums.js /^ OPTION: true$/;" p
45
+ export.SELECT javascript/enums.js /^ SELECT: true$/;" p
46
+ export.TEXTAREA javascript/enums.js /^ TEXTAREA: true,$/;" p
47
+ export.color javascript/enums.js /^ color: true,$/;" p
48
+ export.date javascript/enums.js /^ date: true,$/;" p
49
+ export.datetime javascript/enums.js /^ datetime: true,$/;" p
50
+ export.datetime-local javascript/enums.js /^ 'datetime-local': true,$/;" p
51
+ export.email javascript/enums.js /^ email: true,$/;" p
52
+ export.month javascript/enums.js /^ month: true,$/;" p
53
+ export.number javascript/enums.js /^ number: true,$/;" p
54
+ export.password javascript/enums.js /^ password: true,$/;" p
55
+ export.range javascript/enums.js /^ range: true,$/;" p
56
+ export.search javascript/enums.js /^ search: true,$/;" p
57
+ export.select-multiple javascript/enums.js /^ 'select-multiple': true,$/;" p
58
+ export.select-one javascript/enums.js /^ 'select-one': true,$/;" p
59
+ export.tel javascript/enums.js /^ tel: true,$/;" p
60
+ export.text javascript/enums.js /^ text: true,$/;" p
61
+ export.textarea javascript/enums.js /^ textarea: true,$/;" p
62
+ export.time javascript/enums.js /^ time: true,$/;" p
63
+ export.url javascript/enums.js /^ url: true,$/;" p
64
+ export.week javascript/enums.js /^ week: true$/;" p
65
+ finalizer_for lib/cable_ready/channel.rb /^ def self.finalizer_for(identifier)$/;" F class:CableReady.Channel
66
+ identifier lib/generators/cable_ready/channel_generator.rb /^ def identifier$/;" f class:CableReady
67
+ included test/support/generator_test_helpers.rb /^ def self.included(base)$/;" F class:GeneratorTestHelpers
68
+ initialize lib/cable_ready/channel.rb /^ def initialize(identifier)$/;" f class:CableReady.Channel
55
69
  initialize lib/cable_ready/channels.rb /^ def initialize$/;" f class:CableReady.Channels
56
- mutex lib/cable_ready/channels.rb /^ def mutex$/;" f class:CableReady.Channels
70
+ initialize lib/cable_ready/config.rb /^ def initialize$/;" f class:CableReady.Config
71
+ observers lib/cable_ready/config.rb /^ def observers$/;" f class:CableReady.Config
72
+ operation_names lib/cable_ready/config.rb /^ def operation_names$/;" f class:CableReady.Config
73
+ option_given? lib/generators/cable_ready/channel_generator.rb /^ def option_given?$/;" f class:CableReady
74
+ prepare_destination test/support/generator_test_helpers.rb /^ def prepare_destination$/;" f class:GeneratorTestHelpers.ClassMethods
75
+ remove_sample_app test/support/generator_test_helpers.rb /^ def remove_sample_app$/;" f class:GeneratorTestHelpers.ClassMethods
57
76
  reset lib/cable_ready/channel.rb /^ def reset$/;" f class:CableReady.Channel
77
+ resource lib/generators/cable_ready/channel_generator.rb /^ def resource$/;" f class:CableReady
78
+ sample_app_path test/support/generator_test_helpers.rb /^ def sample_app_path$/;" f class:GeneratorTestHelpers.ClassMethods
79
+ using_broadcast_to? lib/generators/cable_ready/channel_generator.rb /^ def using_broadcast_to?$/;" f class:CableReady
80
+ using_stimulus? lib/generators/cable_ready/channel_generator.rb /^ def using_stimulus?$/;" f class:CableReady
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require_relative "../../../lib/cable_ready"
5
+
6
+ class CableReady::CableCarTest < ActiveSupport::TestCase
7
+ setup do
8
+ @cable_car = CableReady::CableCar.instance
9
+ end
10
+
11
+ test "dispatch should return json-ifiable payload" do
12
+ CableReady::CableCar.instance.reset!
13
+ dispatch = CableReady::CableCar.instance.inner_html(selector: "#users", html: "<span>winning</span>").dispatch
14
+ assert_equal({"innerHtml" => [{"selector" => "#users", "html" => "<span>winning</span>"}]}, dispatch)
15
+ end
16
+
17
+ test "dispatch should clear operations" do
18
+ CableReady::CableCar.instance.reset!
19
+ CableReady::CableCar.instance.inner_html(selector: "#users", html: "<span>winning</span>").dispatch
20
+ assert_equal({}, CableReady::CableCar.instance.instance_variable_get(:@enqueued_operations))
21
+ end
22
+
23
+ test "dispatch should maintain operations if clear is false" do
24
+ CableReady::CableCar.instance.reset!
25
+ CableReady::CableCar.instance.inner_html(selector: "#users", html: "<span>winning</span>").dispatch(clear: false)
26
+ assert_equal({"inner_html" => [{"selector" => "#users", "html" => "<span>winning</span>"}]}, CableReady::CableCar.instance.instance_variable_get(:@enqueued_operations))
27
+ end
28
+
29
+ test "selectors should accept any object which respond_to? to_dom_selector" do
30
+ CableReady::CableCar.instance.reset!
31
+ my_object = Struct.new(:id) do
32
+ def to_dom_selector
33
+ ".#{id}"
34
+ end
35
+ end.new("users")
36
+ dispatch = CableReady::CableCar.instance.inner_html(selector: my_object, html: "<span>winning</span>").dispatch
37
+ assert_equal({"innerHtml" => [{"selector" => ".users", "html" => "<span>winning</span>"}]}, dispatch)
38
+ end
39
+
40
+ test "selectors should accept any object which respond_to? to_dom_id" do
41
+ CableReady::CableCar.instance.reset!
42
+ my_object = Struct.new(:id) do
43
+ def to_dom_id
44
+ id
45
+ end
46
+ end.new("users")
47
+ dispatch = CableReady::CableCar.instance.inner_html(selector: my_object, html: "<span>winning</span>").dispatch
48
+ assert_equal({"innerHtml" => [{"selector" => "#users", "html" => "<span>winning</span>"}]}, dispatch)
49
+ end
50
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require_relative "../../../lib/cable_ready"
5
+
6
+ class User
7
+ include ActiveModel::Model
8
+
9
+ attr_accessor :id
10
+ end
11
+
12
+ class CableReady::IdentifiableTest < ActiveSupport::TestCase
13
+ include CableReady::Identifiable
14
+
15
+ test "should handle nil" do
16
+ assert_equal "#", dom_id(nil)
17
+ end
18
+
19
+ test "should work with strings" do
20
+ assert_equal "#users", dom_id("users")
21
+ assert_equal "#users", dom_id("users ")
22
+ assert_equal "#users", dom_id(" users ")
23
+ end
24
+
25
+ test "should work with symbols" do
26
+ assert_equal "#users", dom_id(:users)
27
+ assert_equal "#active_users", dom_id(:active_users)
28
+ end
29
+
30
+ test "should just return one hash" do
31
+ assert_equal "#users", dom_id("users")
32
+ assert_equal "#users", dom_id("#users")
33
+ assert_equal "#users", dom_id("##users")
34
+ end
35
+
36
+ test "should strip prefixes" do
37
+ assert_equal "#active_users", dom_id(" users ", " active ")
38
+ assert_equal "#all_active_users", dom_id(" users ", " all_active ")
39
+ end
40
+
41
+ test "should not include provided prefix if prefix is nil" do
42
+ assert_equal "#users", dom_id("users", nil)
43
+ end
44
+
45
+ test "should work with ActiveRecord::Relation" do
46
+ relation = mock("ActiveRecord::Relation")
47
+
48
+ relation.stubs(:is_a?).with(ActiveRecord::Relation).returns(true).at_least_once
49
+ relation.stubs(:is_a?).with(ActiveRecord::Base).never
50
+ relation.stubs(:model_name).returns(OpenStruct.new(plural: "users"))
51
+
52
+ assert_equal "#users", dom_id(relation)
53
+ assert_equal "#users", dom_id(relation, nil)
54
+ assert_equal "#active_users", dom_id(relation, "active")
55
+ end
56
+
57
+ test "should work with ActiveRecord::Base" do
58
+ User.any_instance.stubs(:is_a?).with(ActiveRecord::Relation).returns(false)
59
+ User.any_instance.stubs(:is_a?).with(ActiveRecord::Base).returns(true)
60
+
61
+ assert_equal "#new_user", dom_id(User.new(id: nil))
62
+
63
+ user = User.new(id: 42)
64
+
65
+ assert_equal "#user_42", dom_id(user)
66
+ assert_equal "#user_42", dom_id(user, nil)
67
+ assert_equal "#all_active_user_42", dom_id(user, "all_active")
68
+
69
+ user = User.new(id: 99)
70
+
71
+ assert_equal "#user_99", dom_id(user)
72
+ assert_equal "#user_99", dom_id(user, nil)
73
+ assert_equal "#all_active_user_99", dom_id(user, "all_active")
74
+ end
75
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require_relative "../../../lib/cable_ready"
5
+
6
+ class Death
7
+ def to_html
8
+ "I rock"
9
+ end
10
+
11
+ def to_dom_id
12
+ "death"
13
+ end
14
+
15
+ def to_operation_options
16
+ [:html, :dom_id, :spaz]
17
+ end
18
+ end
19
+
20
+ class Life
21
+ def to_operation_options
22
+ {
23
+ html: "You go, girl",
24
+ dom_id: "life"
25
+ }
26
+ end
27
+ end
28
+
29
+ class CableReady::OperationBuilderTest < ActiveSupport::TestCase
30
+ setup do
31
+ @operation_builder = CableReady::OperationBuilder.new("test")
32
+ end
33
+
34
+ test "should create enqueued operations" do
35
+ assert_not_nil @operation_builder.instance_variable_get(:@enqueued_operations)
36
+ end
37
+
38
+ test "should add observer to cable ready" do
39
+ assert_not_nil CableReady.config.instance_variable_get(:@observer_peers)[@operation_builder]
40
+ end
41
+
42
+ test "should remove observer when destroyed" do
43
+ @operation_builder = nil
44
+ assert_nil CableReady.config.instance_variable_get(:@observer_peers)[@operation_builder]
45
+ end
46
+
47
+ test "should add operation method" do
48
+ @operation_builder.add_operation_method("foobar")
49
+ assert @operation_builder.respond_to?(:foobar)
50
+ end
51
+
52
+ test "added operation method should add keys" do
53
+ @operation_builder.add_operation_method("foobar")
54
+ @operation_builder.foobar({name: "passed_option"})
55
+
56
+ operations = @operation_builder.instance_variable_get(:@enqueued_operations)
57
+
58
+ assert_equal 1, operations["foobar"].size
59
+ assert_equal({"name" => "passed_option"}, operations["foobar"].first)
60
+ end
61
+
62
+ test "should json-ify operations" do
63
+ @operation_builder.add_operation_method("foobar")
64
+ @operation_builder.foobar({name: "passed_option"})
65
+ assert_equal("{\"foobar\":[{\"name\":\"passed_option\"}]}", @operation_builder.to_json)
66
+ end
67
+
68
+ test "should apply! many operations" do
69
+ @operation_builder.apply!(foobar: [{name: "passed_option"}])
70
+
71
+ operations = @operation_builder.instance_variable_get(:@enqueued_operations)
72
+ assert_equal 1, operations["foobar"].size
73
+ assert_equal({"name" => "passed_option"}, operations["foobar"].first)
74
+ end
75
+
76
+ test "should apply! many operations from a string" do
77
+ @operation_builder.apply!(JSON.generate({foobar: [{name: "passed_option"}]}))
78
+
79
+ operations = @operation_builder.instance_variable_get(:@enqueued_operations)
80
+ assert_equal 1, operations["foobar"].size
81
+ assert_equal({"name" => "passed_option"}, operations["foobar"].first)
82
+ end
83
+
84
+ test "operations payload should omit empty operations" do
85
+ @operation_builder.add_operation_method("foobar")
86
+ payload = @operation_builder.operations_payload
87
+ assert_equal({}, payload)
88
+ end
89
+
90
+ test "operations payload should camelize keys" do
91
+ @operation_builder.add_operation_method("foo_bar")
92
+ @operation_builder.foo_bar({beep_boop: "passed_option"})
93
+ assert_equal({"fooBar" => [{"beepBoop" => "passed_option"}]}, @operation_builder.operations_payload)
94
+ end
95
+
96
+ test "should take first argument as selector" do
97
+ @operation_builder.add_operation_method("inner_html")
98
+
99
+ @operation_builder.inner_html("#smelly", html: "<span>I rock</span>")
100
+
101
+ operations = {
102
+ "innerHtml" => [{"html" => "<span>I rock</span>", "selector" => "#smelly"}]
103
+ }
104
+
105
+ assert_equal(operations, @operation_builder.operations_payload)
106
+ end
107
+
108
+ test "should use previously passed selector in next operation" do
109
+ @operation_builder.add_operation_method("inner_html")
110
+ @operation_builder.add_operation_method("set_focus")
111
+
112
+ @operation_builder.set_focus("#smelly").inner_html(html: "<span>I rock</span>")
113
+
114
+ operations = {
115
+ "setFocus" => [{"selector" => "#smelly"}],
116
+ "innerHtml" => [{"html" => "<span>I rock</span>", "selector" => "#smelly"}]
117
+ }
118
+
119
+ assert_equal(operations, @operation_builder.operations_payload)
120
+ end
121
+
122
+ test "should clear previous_selector after calling reset!" do
123
+ @operation_builder.add_operation_method("inner_html")
124
+ @operation_builder.inner_html(selector: "#smelly", html: "<span>I rock</span>")
125
+
126
+ @operation_builder.reset!
127
+
128
+ @operation_builder.inner_html(html: "<span>winning</span>")
129
+
130
+ assert_equal({"innerHtml" => [{"html" => "<span>winning</span>"}]}, @operation_builder.operations_payload)
131
+ end
132
+
133
+ test "should use previous_selector if present and should use `selector` if explicitly provided" do
134
+ @operation_builder.add_operation_method("inner_html")
135
+ @operation_builder.add_operation_method("set_focus")
136
+
137
+ @operation_builder.set_focus("#smelly").inner_html(html: "<span>I rock</span>").inner_html(html: "<span>I rock too</span>", selector: "#smelly2")
138
+
139
+ operations = {
140
+ "setFocus" => [
141
+ {"selector" => "#smelly"}
142
+ ],
143
+ "innerHtml" => [
144
+ {"html" => "<span>I rock</span>", "selector" => "#smelly"},
145
+ {"html" => "<span>I rock too</span>", "selector" => "#smelly2"}
146
+ ]
147
+ }
148
+
149
+ assert_equal(operations, @operation_builder.operations_payload)
150
+ end
151
+
152
+ test "should pull html option from Death object" do
153
+ @operation_builder.add_operation_method("inner_html")
154
+ death = Death.new
155
+
156
+ @operation_builder.inner_html(html: death)
157
+
158
+ operations = {
159
+ "innerHtml" => [
160
+ {"html" => "I rock"}
161
+ ]
162
+ }
163
+
164
+ assert_equal(operations, @operation_builder.operations_payload)
165
+ end
166
+
167
+ test "should pull html option with selector from Death object" do
168
+ @operation_builder.add_operation_method("inner_html")
169
+ death = Death.new
170
+
171
+ @operation_builder.inner_html(death, html: death)
172
+
173
+ operations = {
174
+ "innerHtml" => [
175
+ {"html" => "I rock", "selector" => "#death"}
176
+ ]
177
+ }
178
+
179
+ assert_equal(operations, @operation_builder.operations_payload)
180
+ end
181
+
182
+ test "should pull html and dom_id options from Death object" do
183
+ @operation_builder.add_operation_method("inner_html")
184
+ death = Death.new
185
+
186
+ @operation_builder.inner_html(death)
187
+
188
+ operations = {
189
+ "innerHtml" => [
190
+ {"html" => "I rock", "domId" => "death"}
191
+ ]
192
+ }
193
+
194
+ assert_equal(operations, @operation_builder.operations_payload)
195
+ end
196
+
197
+ test "should pull html and dom_id options from Life object" do
198
+ @operation_builder.add_operation_method("inner_html")
199
+ life = Life.new
200
+
201
+ @operation_builder.inner_html(life)
202
+
203
+ operations = {
204
+ "innerHtml" => [
205
+ {"html" => "You go, girl", "domId" => "life"}
206
+ ]
207
+ }
208
+
209
+ assert_equal(operations, @operation_builder.operations_payload)
210
+ end
211
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "generators/cable_ready/channel_generator"
5
+
6
+ class CableReady::ChannelGeneratorTest < Rails::Generators::TestCase
7
+ include ::GeneratorTestHelpers
8
+
9
+ tests CableReady::ChannelGenerator
10
+ destination File.expand_path("../../../../tmp/dummy", __dir__)
11
+
12
+ prepare_destination
13
+ create_sample_app
14
+
15
+ MiniTest.after_run do
16
+ remove_sample_app
17
+ end
18
+
19
+ test "should generate channel with the same resource name and stimulus controller" do
20
+ run_generator ["user", "--stream-for=user", "--stimulus"]
21
+
22
+ assert_file "app/channels/user_channel.rb" do |content|
23
+ assert_match(/class\ UserChannel\ </, content)
24
+ assert_match(/stream_for\ User\.find\(params\[:id\]\)/, content)
25
+ end
26
+
27
+ assert_file "app/javascript/channels/user_channel.js", /"UserChannel"/
28
+ assert_file "app/javascript/controllers/user_controller.js", /'UserChannel'/
29
+ end
30
+
31
+ test "should generate channel with different resource name" do
32
+ run_generator ["my_name", "--stream-for=under_scored_resource_name", "--no-stimulus"]
33
+
34
+ assert_file "app/channels/my_name_channel.rb" do |content|
35
+ assert_match(/class\ MyNameChannel\ </, content)
36
+ assert_match(/stream_for\ UnderScoredResourceName\.find\(params\[:id\]\)/, content)
37
+ end
38
+
39
+ assert_file "app/javascript/channels/my_name_channel.js", /"MyNameChannel"/
40
+ assert_no_file "app/javascript/controllers/my_name_controller.js"
41
+ end
42
+
43
+ test "should not generate stimulus controller if not requested" do
44
+ run_generator ["comment", "--stream-for=comment", "--no-stimulus"]
45
+
46
+ assert_file "app/channels/comment_channel.rb"
47
+ assert_file "app/javascript/channels/comment_channel.js"
48
+ assert_no_file "app/javascript/controllers/comment_controller.js"
49
+ end
50
+
51
+ test "should run the generator when streaming from identifier" do
52
+ run_generator ["page", "--stream-from=page"]
53
+
54
+ assert_file "app/channels/page_channel.rb" do |content|
55
+ assert_match(/PageChannel/, content)
56
+ assert_match(/stream_from\ "page"/, content)
57
+ end
58
+
59
+ assert_file "app/javascript/channels/page_channel.js" do |content|
60
+ assert_match(/"PageChannel"/, content)
61
+ assert_match(/import\ CableReady/, content)
62
+ assert_match(/if\ \(data\.cableReady\)\ CableReady\.perform\(data\.operations\)/, content)
63
+ end
64
+ end
65
+
66
+ test "should run the generator when streaming without resource and different identifier" do
67
+ run_generator ["my_page", "--stream-from=ThisIsMyPage"]
68
+
69
+ assert_file "app/channels/my_page_channel.rb" do |content|
70
+ assert_match(/MyPageChannel/, content)
71
+ assert_match(/stream_from\ "this_is_my_page"/, content)
72
+ end
73
+
74
+ assert_file "app/javascript/channels/my_page_channel.js" do |content|
75
+ assert_match(/"MyPageChannel"/, content)
76
+ assert_match(/import\ CableReady/, content)
77
+ assert_match(/if\ \(data\.cableReady\)\ CableReady\.perform\(data\.operations\)/, content)
78
+ end
79
+ end
80
+
81
+ test "should run the generator and use the NAME for --stream-from if nothing passed" do
82
+ run_generator ["house", "--stream-from"]
83
+
84
+ assert_file "app/channels/house_channel.rb" do |content|
85
+ assert_match(/HouseChannel/, content)
86
+ assert_match(/stream_from\ "house"/, content)
87
+ end
88
+
89
+ assert_file "app/javascript/channels/house_channel.js" do |content|
90
+ assert_match(/"HouseChannel"/, content)
91
+ assert_match(/import\ CableReady/, content)
92
+ assert_match(/if\ \(data\.cableReady\)\ CableReady\.perform\(data\.operations\)/, content)
93
+ end
94
+ end
95
+
96
+ test "should run the generator and use the NAME for --stream-for if nothing passed" do
97
+ run_generator ["option", "--stream-for", "--stimulus"]
98
+
99
+ assert_file "app/channels/option_channel.rb" do |content|
100
+ assert_match(/class\ OptionChannel\ </, content)
101
+ assert_match(/stream_for\ Option\.find\(params\[:id\]\)/, content)
102
+ end
103
+
104
+ assert_file "app/javascript/channels/option_channel.js", /"OptionChannel"/
105
+ assert_file "app/javascript/controllers/option_controller.js", /'OptionChannel'/
106
+ end
107
+
108
+ test "should run not generate anything if passed stream_from and stream_for" do
109
+ assert_raises "Can't specify --stream-from and --stream-for at the same time" do
110
+ run_generator ["error", "--stream-from=1", "--stream-for=2"]
111
+ end
112
+ end
113
+
114
+ # some tests without generator options to simulate the inputs passed via cli
115
+
116
+ test "should generate channel with the same resource name and stimulus controller (without options)" do
117
+ CableReady::ChannelGenerator.any_instance.stubs(:using_broadcast_to?).returns(true)
118
+ CableReady::ChannelGenerator.any_instance.stubs(:resource).returns("Post")
119
+ CableReady::ChannelGenerator.any_instance.stubs(:using_stimulus?).returns(true)
120
+
121
+ run_generator ["post"]
122
+
123
+ assert_file "app/channels/post_channel.rb" do |content|
124
+ assert_match(/class\ PostChannel\ </, content)
125
+ assert_match(/stream_for\ Post\.find\(params\[:id\]\)/, content)
126
+ end
127
+
128
+ assert_file "app/javascript/channels/post_channel.js", /PostChannel/
129
+ assert_file "app/javascript/controllers/post_controller.js", /'PostChannel'/
130
+ end
131
+
132
+ test "should not generate stimulus controller if not requested (without options)" do
133
+ CableReady::ChannelGenerator.any_instance.stubs(:using_broadcast_to?).returns(true)
134
+ CableReady::ChannelGenerator.any_instance.stubs(:resource).returns("Admin")
135
+ CableReady::ChannelGenerator.any_instance.stubs(:using_stimulus?).returns(false)
136
+
137
+ run_generator ["admin"]
138
+
139
+ assert_file "app/channels/admin_channel.rb"
140
+ assert_file "app/javascript/channels/admin_channel.js"
141
+ assert_no_file "app/javascript/controllers/admin_controller.js"
142
+ end
143
+
144
+ test "should run the generator when streaming from identifier (without options)" do
145
+ CableReady::ChannelGenerator.any_instance.stubs(:using_broadcast_to?).returns(false)
146
+ CableReady::ChannelGenerator.any_instance.stubs(:identifier).returns("index_identifier")
147
+
148
+ run_generator ["index"]
149
+
150
+ assert_file "app/channels/index_channel.rb" do |content|
151
+ assert_match(/IndexChannel/, content)
152
+ assert_match(/stream_from\ "index_identifier"/, content)
153
+ end
154
+
155
+ assert_file "app/javascript/channels/index_channel.js", /"IndexChannel"/
156
+ end
157
+ end