stimulus_reflex 3.4.0.pre5 → 3.4.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.

Potentially problematic release.


This version of stimulus_reflex might be problematic. Click here for more details.

Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -2
  3. data/Gemfile.lock +76 -73
  4. data/README.md +5 -8
  5. data/Rakefile +5 -5
  6. data/app/channels/stimulus_reflex/channel.rb +22 -0
  7. data/bin/console +1 -0
  8. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/%file_name%_controller.js.tt +14 -2
  9. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application_controller.js.tt +10 -2
  10. data/lib/generators/stimulus_reflex/templates/app/reflexes/%file_name%_reflex.rb.tt +19 -9
  11. data/lib/generators/stimulus_reflex/templates/app/reflexes/application_reflex.rb.tt +2 -2
  12. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +10 -1
  13. data/lib/stimulus_reflex.rb +2 -1
  14. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +2 -4
  15. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +2 -1
  16. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +2 -2
  17. data/lib/stimulus_reflex/cable_ready_channels.rb +21 -0
  18. data/lib/stimulus_reflex/configuration.rb +2 -1
  19. data/lib/stimulus_reflex/reflex.rb +16 -6
  20. data/lib/stimulus_reflex/sanity_checker.rb +18 -4
  21. data/lib/stimulus_reflex/version.rb +1 -1
  22. data/lib/tasks/stimulus_reflex/install.rake +8 -3
  23. data/package.json +3 -3
  24. data/stimulus_reflex.gemspec +3 -1
  25. data/tags +95 -37
  26. data/test/broadcasters/broadcaster_test.rb +3 -6
  27. data/test/broadcasters/broadcaster_test_case.rb +15 -0
  28. data/test/broadcasters/nothing_broadcaster_test.rb +27 -26
  29. data/test/broadcasters/page_broadcaster_test.rb +56 -50
  30. data/test/broadcasters/selector_broadcaster_test.rb +57 -83
  31. data/test/reflex_test.rb +32 -0
  32. data/test/test_helper.rb +43 -2
  33. data/test/tmp/app/reflexes/application_reflex.rb +2 -2
  34. data/test/tmp/app/reflexes/demo_reflex.rb +34 -0
  35. data/yarn.lock +199 -226
  36. metadata +13 -8
  37. data/test/tmp/app/reflexes/posts_reflex.rb +0 -24
@@ -1,11 +1,8 @@
1
- require_relative "../test_helper"
1
+ # frozen_string_literal: true
2
2
 
3
- class StimulusReflex::BroadcasterTest < ActiveSupport::TestCase
4
- setup do
5
- @reflex = Minitest::Mock.new
6
- @reflex.expect :stream_name, "TestStream"
7
- end
3
+ require_relative "broadcaster_test_case"
8
4
 
5
+ class StimulusReflex::BroadcasterTest < StimulusReflex::BroadcasterTestCase
9
6
  test "raises a NotImplementedError if called directly" do
10
7
  broadcaster = StimulusReflex::Broadcaster.new(@reflex)
11
8
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+
5
+ class StimulusReflex::BroadcasterTestCase < ActionCable::Channel::TestCase
6
+ tests StimulusReflex::Channel
7
+
8
+ setup do
9
+ stub_connection(session_id: SecureRandom.uuid)
10
+ def connection.env
11
+ @env ||= {}
12
+ end
13
+ @reflex = StimulusReflex::Reflex.new(subscribe, url: "https://test.stimulusreflex.com")
14
+ end
15
+ end
@@ -1,34 +1,35 @@
1
- require_relative "../test_helper"
1
+ # frozen_string_literal: true
2
2
 
3
- class StimulusReflex::NothingBroadcasterTest < ActiveSupport::TestCase
4
- setup do
5
- @reflex = Minitest::Mock.new
6
- @reflex.expect :stream_name, "TestStream"
7
- end
3
+ require_relative "broadcaster_test_case"
8
4
 
5
+ class StimulusReflex::NothingBroadcasterTest < StimulusReflex::BroadcasterTestCase
9
6
  test "broadcasts a server message when called" do
10
7
  broadcaster = StimulusReflex::NothingBroadcaster.new(@reflex)
11
8
 
12
- cable_ready_channels = Minitest::Mock.new
13
- cable_ready_channel = Minitest::Mock.new
14
- CableReady::Channels.stub :instance, cable_ready_channels do
15
- cable_ready_channel.expect(:dispatch_event, nil, [{name: "stimulus-reflex:server-message",
16
- detail: {
17
- reflexId: nil,
18
- stimulus_reflex: {
19
- some: :data,
20
- morph: :nothing,
21
- server_message: {
22
- subject: "nothing", body: nil
23
- }
24
- }
25
- }}])
26
- cable_ready_channels.expect(:[], cable_ready_channel, ["TestStream"])
27
- cable_ready_channels.expect(:broadcast, nil)
28
- broadcaster.broadcast(nil, {some: :data})
29
- end
9
+ expected = {
10
+ "cableReady" => true,
11
+ "operations" => {
12
+ "dispatchEvent" => [
13
+ {
14
+ "name" => "stimulus-reflex:server-message",
15
+ "detail" => {
16
+ "reflexId" => nil,
17
+ "stimulusReflex" => {
18
+ "some" => :data,
19
+ "morph" => :nothing,
20
+ "serverMessage" => {
21
+ "subject" => "nothing",
22
+ "body" => nil
23
+ }
24
+ }
25
+ }
26
+ }
27
+ ]
28
+ }
29
+ }
30
30
 
31
- assert_mock cable_ready_channels
32
- assert_mock cable_ready_channel
31
+ assert_broadcast_on @reflex.stream_name, expected do
32
+ broadcaster.broadcast nil, some: :data
33
+ end
33
34
  end
34
35
  end
@@ -1,69 +1,75 @@
1
- require_relative "../test_helper"
1
+ # frozen_string_literal: true
2
2
 
3
- class StimulusReflex::PageBroadcasterTest < ActiveSupport::TestCase
4
- setup do
5
- @reflex = Minitest::Mock.new
6
- @reflex.expect :params, {action: "show"}
7
- @reflex.expect :stream_name, "TestStream"
8
- @reflex.expect :permanent_attribute_name, "some-attribute"
9
- end
3
+ require_relative "broadcaster_test_case"
10
4
 
5
+ class StimulusReflex::PageBroadcasterTest < StimulusReflex::BroadcasterTestCase
11
6
  test "returns if the response html is empty" do
12
- controller = Minitest::Mock.new
13
- controller.expect(:process, nil, ["show"])
14
- @reflex.expect :controller, controller
15
- @reflex.expect :controller, controller
7
+ broadcaster = StimulusReflex::PageBroadcaster.new(@reflex)
8
+ broadcaster.broadcast(["#foo"], {some: :data})
9
+ # TODO: figure out how to refute_broadcast_on
10
+ end
16
11
 
17
- # stub the controller response with a struct responding to :body
18
- controller.expect(:response, Struct.new(:body).new(nil))
12
+ test "performs a page morph on body" do
13
+ class << @reflex.controller.response
14
+ def body
15
+ "<html><head></head><body>New Content</body></html>"
16
+ end
17
+ end
19
18
 
20
19
  broadcaster = StimulusReflex::PageBroadcaster.new(@reflex)
21
20
 
22
- cable_ready_channels = Minitest::Mock.new
23
- cable_ready_channels.expect(:broadcast, nil)
24
-
25
- broadcaster.broadcast(["#foo"], {some: :data})
21
+ expected = {
22
+ "cableReady" => true,
23
+ "operations" => {
24
+ "morph" => [
25
+ {
26
+ "selector" => "body",
27
+ "html" => "New Content",
28
+ "childrenOnly" => true,
29
+ "permanentAttributeName" => nil,
30
+ "stimulusReflex" => {
31
+ "some" => :data,
32
+ "morph" => :page
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ }
26
38
 
27
- assert_raises { cable_ready_channels.verify }
39
+ assert_broadcast_on @reflex.stream_name, expected do
40
+ broadcaster.broadcast(["body"], {some: :data})
41
+ end
28
42
  end
29
43
 
30
44
  test "performs a page morph given an array of reflex root selectors" do
31
- controller = Minitest::Mock.new
32
- controller.expect(:process, nil, ["show"])
33
- @reflex.expect :controller, controller
34
- @reflex.expect :controller, controller
35
-
36
- # stub the controller response with a struct responding to :body
37
- controller.expect(:response, Struct.new(:body).new("<html></html>"))
45
+ class << @reflex.controller.response
46
+ def body
47
+ "<html><head></head><body><div id=\"foo\">New Content</div></body></html>"
48
+ end
49
+ end
38
50
 
39
51
  broadcaster = StimulusReflex::PageBroadcaster.new(@reflex)
40
52
 
41
- cable_ready_channels = Minitest::Mock.new
42
- cable_ready_channel = Minitest::Mock.new
43
- document = Minitest::Mock.new
44
- Nokogiri::HTML.stub :parse, document do
45
- document.expect(:css, "something that is present", ["#foo"])
46
- document.expect(:css, Struct.new(:inner_html).new("<span>bar</span>"), ["#foo"])
47
-
48
- CableReady::Channels.stub :instance, cable_ready_channels do
49
- cable_ready_channel.expect(:morph, nil, [{
50
- selector: "#foo",
51
- html: "<span>bar</span>",
52
- children_only: true,
53
- permanent_attribute_name: "some-attribute",
54
- stimulus_reflex: {
55
- some: :data,
56
- morph: :page
53
+ expected = {
54
+ "cableReady" => true,
55
+ "operations" => {
56
+ "morph" => [
57
+ {
58
+ "selector" => "#foo",
59
+ "html" => "New Content",
60
+ "childrenOnly" => true,
61
+ "permanentAttributeName" => nil,
62
+ "stimulusReflex" => {
63
+ "some" => :data,
64
+ "morph" => :page
65
+ }
57
66
  }
58
- }])
59
- cable_ready_channels.expect(:[], cable_ready_channel, ["TestStream"])
60
- cable_ready_channels.expect(:broadcast, nil)
67
+ ]
68
+ }
69
+ }
61
70
 
62
- broadcaster.broadcast(["#foo"], {some: :data})
63
- end
71
+ assert_broadcast_on @reflex.stream_name, expected do
72
+ broadcaster.broadcast(["#foo"], {some: :data})
64
73
  end
65
-
66
- assert_mock cable_ready_channels
67
- assert_mock cable_ready_channel
68
74
  end
69
75
  end
@@ -1,83 +1,57 @@
1
- require_relative "../test_helper"
2
-
3
- class StimulusReflex::SelectorBroadcasterTest < ActiveSupport::TestCase
4
- setup do
5
- @reflex = Minitest::Mock.new
6
- @reflex.expect :stream_name, "TestStream"
7
- @reflex.expect :permanent_attribute_name, "some-attribute"
8
- end
9
-
10
- test "morphs the contents of an element if the selector(s) are present in both original and morphed html fragments" do
11
- broadcaster = StimulusReflex::SelectorBroadcaster.new(@reflex)
12
-
13
- cable_ready_channels = Minitest::Mock.new
14
- cable_ready_channel = Minitest::Mock.new
15
- fragment = Minitest::Mock.new
16
- match = Minitest::Mock.new
17
- Nokogiri::HTML.stub :fragment, fragment do
18
- fragment.expect(:at_css, match, ["#foo"])
19
- match.expect(:present?, true)
20
-
21
- # we need to mock `!`, because `blank?` returns
22
- # respond_to?(:empty?) ? !!empty? : !self
23
- match.expect(:!, false)
24
- match.expect(:inner_html, "<span>bar</span>")
25
- CableReady::Channels.stub :instance, cable_ready_channels do
26
- broadcaster.append_morph("#foo", "<div id=\"foo\"><span>bar</span></div>")
27
- cable_ready_channel.expect(:morph, nil, [{
28
- selector: "#foo",
29
- html: "<span>bar</span>",
30
- children_only: true,
31
- permanent_attribute_name: "some-attribute",
32
- stimulus_reflex: {
33
- some: :data,
34
- morph: :selector
35
- }
36
- }])
37
- cable_ready_channels.expect(:[], cable_ready_channel, ["TestStream"])
38
- cable_ready_channels.expect(:broadcast, nil)
39
-
40
- broadcaster.broadcast(nil, {some: :data})
41
- end
42
- end
43
-
44
- assert_mock cable_ready_channels
45
- assert_mock cable_ready_channel
46
- end
47
-
48
- test "replaces the contents of an element and ignores permanent-attributes if the selector(s) aren't present in the replacing html fragment" do
49
- broadcaster = StimulusReflex::SelectorBroadcaster.new(@reflex)
50
-
51
- cable_ready_channels = Minitest::Mock.new
52
- cable_ready_channel = Minitest::Mock.new
53
- fragment = Minitest::Mock.new
54
- match = Minitest::Mock.new
55
- Nokogiri::HTML.stub :fragment, fragment do
56
- fragment.expect(:at_css, match, ["#foo"])
57
- fragment.expect(:to_html, "<div id=\"baz\"><span>bar</span></div>")
58
- match.expect(:present?, false)
59
-
60
- # we need to mock `!`, because `blank?` returns
61
- # respond_to?(:empty?) ? !!empty? : !self
62
- match.expect(:!, true)
63
- CableReady::Channels.stub :instance, cable_ready_channels do
64
- broadcaster.append_morph("#foo", "<div id=\"baz\"><span>bar</span></div>")
65
- cable_ready_channel.expect(:inner_html, nil, [{
66
- selector: "#foo",
67
- html: "<div id=\"baz\"><span>bar</span></div>",
68
- stimulus_reflex: {
69
- some: :data,
70
- morph: :selector
71
- }
72
- }])
73
- cable_ready_channels.expect(:[], cable_ready_channel, ["TestStream"])
74
- cable_ready_channels.expect(:broadcast, nil)
75
-
76
- broadcaster.broadcast(nil, {some: :data})
77
- end
78
- end
79
-
80
- assert_mock cable_ready_channels
81
- assert_mock cable_ready_channel
82
- end
83
- end
1
+ # frozen_string_literal: true
2
+
3
+ # require_relative "broadcaster_test_case"
4
+
5
+ # class StimulusReflex::SelectorBroadcasterTest < StimulusReflex::BroadcasterTestCase
6
+ # test "morphs the contents of an element if the selector(s) are present in both original and morphed html fragments" do
7
+ # broadcaster = StimulusReflex::SelectorBroadcaster.new(@reflex)
8
+ # broadcaster.append_morph("#foo", "<div id=\"foo\"><span>bar</span></div>")
9
+
10
+ # expected = {
11
+ # "cableReady" => true,
12
+ # "operations" => {
13
+ # "morph" => [
14
+ # {
15
+ # "selector" => "#foo",
16
+ # "html" => "<span>bar</span>",
17
+ # "childrenOnly" => true,
18
+ # "permanentAttributeName" => nil,
19
+ # "stimulusReflex" => {
20
+ # "some" => :data,
21
+ # "morph" => :selector
22
+ # }
23
+ # }
24
+ # ]
25
+ # }
26
+ # }
27
+
28
+ # assert_broadcast_on @reflex.stream_name, expected do
29
+ # broadcaster.broadcast nil, some: :data
30
+ # end
31
+ # end
32
+
33
+ # test "replaces the contents of an element and ignores permanent-attributes if the selector(s) aren't present in the replacing html fragment" do
34
+ # broadcaster = StimulusReflex::SelectorBroadcaster.new(@reflex)
35
+ # broadcaster.append_morph("#foo", "<div id=\"baz\"><span>bar</span></div>")
36
+
37
+ # expected = {
38
+ # "cableReady" => true,
39
+ # "operations" => {
40
+ # "innerHtml" => [
41
+ # {
42
+ # "selector" => "#foo",
43
+ # "html" => "<div id=\"baz\"><span>bar</span></div>",
44
+ # "stimulusReflex" => {
45
+ # "some" => :data,
46
+ # "morph" => :selector
47
+ # }
48
+ # }
49
+ # ]
50
+ # }
51
+ # }
52
+
53
+ # assert_broadcast_on @reflex.stream_name, expected do
54
+ # broadcaster.broadcast nil, some: :data
55
+ # end
56
+ # end
57
+ # end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class StimulusReflex::ReflexTest < ActionCable::Channel::TestCase
6
+ tests StimulusReflex::Channel
7
+
8
+ setup do
9
+ stub_connection(session_id: SecureRandom.uuid)
10
+ def connection.env
11
+ @env ||= {}
12
+ end
13
+ @reflex = StimulusReflex::Reflex.new(subscribe, url: "https://test.stimulusreflex.com")
14
+ @reflex.controller_class.view_paths << Rails.root.join("test/views")
15
+ end
16
+
17
+ test "render plain" do
18
+ assert @reflex.render(plain: "Some text") == "Some text"
19
+ end
20
+
21
+ test "render template" do
22
+ assert @reflex.render("/hello_template", assigns: {message: "Testing 123"}) == "<p>Hello from template! Testing 123</p>\n"
23
+ end
24
+
25
+ test "render partial" do
26
+ assert @reflex.render(partial: "/hello_partial", assigns: {message: "Testing 123"}) == "<p>Hello from partial! Testing 123</p>\n"
27
+ end
28
+
29
+ test "dom_id" do
30
+ assert @reflex.dom_id(TestModel.new(id: 123)) == "test_model_123"
31
+ end
32
+ end
@@ -1,6 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "minitest/mock"
4
-
5
3
  ENV["RAILS_ENV"] ||= "test"
4
+
5
+ require "minitest/mock"
6
+ require "rails"
7
+ require "active_model"
8
+ require "action_controller"
9
+ require "pry"
6
10
  require_relative "../lib/stimulus_reflex"
11
+
12
+ class TestApp < Rails::Application
13
+ routes.draw { root to: "test#index" }
14
+ end
15
+
16
+ class ApplicationController < ActionController::Base; end
17
+
18
+ class TestController < ApplicationController
19
+ include Rails.application.routes.url_helpers
20
+
21
+ def index
22
+ head :ok
23
+ end
24
+ end
25
+
26
+ class SessionMock
27
+ def load!
28
+ nil
29
+ end
30
+ end
31
+
32
+ class ActionDispatch::Request
33
+ def session
34
+ @session ||= SessionMock.new
35
+ end
36
+ end
37
+
38
+ class TestModel
39
+ include ActiveModel::Model
40
+ attr_accessor :id
41
+ end
42
+
43
+ StimulusReflex.configuration.parent_channel = "ActionCable::Channel::Base"
44
+ ActionCable::Server::Base.config.cable = {adapter: "test"}
45
+ ActionCable::Server::Base.config.logger = Logger.new(nil)
46
+
47
+ require_relative "../app/channels/stimulus_reflex/channel"
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ApplicationReflex < StimulusReflex::Reflex
4
- # Put application wide Reflex behavior in this file.
4
+ # Put application-wide Reflex behavior and callbacks in this file.
5
5
  #
6
6
  # Example:
7
7
  #
8
8
  # # If your ActionCable connection is: `identified_by :current_user`
9
9
  # delegate :current_user, to: :connection
10
10
  #
11
- # Learn more at: https://docs.stimulusreflex.com
11
+ # Learn more at: https://docs.stimulusreflex.com/reflexes#reflex-classes
12
12
  end