hyper-react 0.99.6 → 1.0.0.lap21

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.
Files changed (187) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +30 -37
  4. data/.rubocop.yml +1159 -0
  5. data/.travis.yml +32 -0
  6. data/Appraisals +31 -0
  7. data/CHANGELOG.md +143 -0
  8. data/DOCS.md +1515 -0
  9. data/Gemfile +2 -5
  10. data/LICENSE +19 -0
  11. data/README.md +5 -33
  12. data/Rakefile +25 -6
  13. data/UPGRADING.md +24 -0
  14. data/component-name-lookup.md +145 -0
  15. data/dciy.toml +3 -0
  16. data/dciy_prepare.sh +8 -0
  17. data/dciy_run.sh +10 -0
  18. data/hyper-react.gemspec +24 -18
  19. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
  20. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
  21. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  22. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  23. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  24. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  25. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  26. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  27. data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  28. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
  29. data/lib/hyper-react.rb +66 -4
  30. data/lib/rails-helpers/top_level_rails_component.rb +75 -0
  31. data/lib/react/api.rb +203 -0
  32. data/lib/react/callbacks.rb +41 -0
  33. data/lib/react/children.rb +30 -0
  34. data/lib/react/component.rb +177 -0
  35. data/lib/react/component/api.rb +69 -0
  36. data/lib/react/component/base.rb +13 -0
  37. data/lib/react/component/class_methods.rb +181 -0
  38. data/lib/react/component/dsl_instance_methods.rb +23 -0
  39. data/lib/react/component/params.rb +6 -0
  40. data/lib/react/component/props_wrapper.rb +78 -0
  41. data/lib/react/component/should_component_update.rb +99 -0
  42. data/lib/react/component/tags.rb +108 -0
  43. data/lib/react/config.rb +5 -0
  44. data/lib/react/config/client.rb.erb +19 -0
  45. data/lib/react/config/server.rb +23 -0
  46. data/lib/react/element.rb +150 -0
  47. data/lib/react/event.rb +76 -0
  48. data/lib/react/ext/hash.rb +9 -0
  49. data/lib/react/ext/opal-jquery/element.rb +26 -0
  50. data/lib/react/ext/string.rb +8 -0
  51. data/lib/react/hash.rb +13 -0
  52. data/lib/react/native_library.rb +87 -0
  53. data/lib/react/object.rb +15 -0
  54. data/lib/react/react-source-browser.rb +3 -0
  55. data/lib/react/react-source-server.rb +3 -0
  56. data/lib/react/react-source.rb +16 -0
  57. data/lib/react/ref_callback.rb +31 -0
  58. data/lib/react/rendering_context.rb +146 -0
  59. data/lib/react/server.rb +19 -0
  60. data/lib/react/state_wrapper.rb +23 -0
  61. data/lib/react/test.rb +16 -0
  62. data/lib/react/test/dsl.rb +17 -0
  63. data/lib/react/test/matchers/render_html_matcher.rb +56 -0
  64. data/lib/react/test/rspec.rb +15 -0
  65. data/lib/react/test/session.rb +37 -0
  66. data/lib/react/test/utils.rb +71 -0
  67. data/lib/react/top_level.rb +110 -0
  68. data/lib/react/top_level_render.rb +28 -0
  69. data/lib/react/validator.rb +136 -0
  70. data/lib/reactive-ruby/component_loader.rb +43 -0
  71. data/lib/reactive-ruby/isomorphic_helpers.rb +235 -0
  72. data/lib/reactive-ruby/rails.rb +8 -0
  73. data/lib/reactive-ruby/rails/component_mount.rb +48 -0
  74. data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
  75. data/lib/reactive-ruby/rails/railtie.rb +20 -0
  76. data/lib/reactive-ruby/serializers.rb +15 -0
  77. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
  78. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
  79. data/lib/reactive-ruby/version.rb +3 -0
  80. data/lib/reactrb/auto-import.rb +27 -0
  81. data/logo1.png +0 -0
  82. data/logo2.png +0 -0
  83. data/logo3.png +0 -0
  84. data/path_release_steps.md +9 -0
  85. data/spec/controller_helper_spec.rb +35 -0
  86. data/spec/index.html.erb +11 -0
  87. data/spec/react/callbacks_spec.rb +142 -0
  88. data/spec/react/children_spec.rb +132 -0
  89. data/spec/react/component/base_spec.rb +36 -0
  90. data/spec/react/component_spec.rb +1073 -0
  91. data/spec/react/dsl_spec.rb +323 -0
  92. data/spec/react/element_spec.rb +132 -0
  93. data/spec/react/event_spec.rb +39 -0
  94. data/spec/react/native_library_spec.rb +387 -0
  95. data/spec/react/observable_spec.rb +31 -0
  96. data/spec/react/opal_jquery_extensions_spec.rb +68 -0
  97. data/spec/react/param_declaration_spec.rb +253 -0
  98. data/spec/react/react_spec.rb +278 -0
  99. data/spec/react/refs_callback_spec.rb +65 -0
  100. data/spec/react/server_spec.rb +25 -0
  101. data/spec/react/state_spec.rb +52 -0
  102. data/spec/react/test/dsl_spec.rb +43 -0
  103. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  104. data/spec/react/test/rspec_spec.rb +62 -0
  105. data/spec/react/test/session_spec.rb +88 -0
  106. data/spec/react/test/utils_spec.rb +28 -0
  107. data/spec/react/top_level_component_spec.rb +103 -0
  108. data/spec/react/tutorial/tutorial_spec.rb +42 -0
  109. data/spec/react/validator_spec.rb +134 -0
  110. data/spec/reactive-ruby/component_loader_spec.rb +74 -0
  111. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
  112. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
  113. data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
  114. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
  115. data/spec/spec_helper.rb +55 -0
  116. data/spec/test_app/README.md +24 -0
  117. data/spec/test_app/Rakefile +6 -0
  118. data/spec/test_app/app/assets/config/manifest.js +3 -0
  119. data/spec/test_app/app/assets/images/.keep +0 -0
  120. data/spec/test_app/app/assets/javascripts/application.rb +7 -0
  121. data/spec/test_app/app/assets/javascripts/cable.js +13 -0
  122. data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
  123. data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
  124. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  125. data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
  126. data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
  127. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  128. data/spec/test_app/app/controllers/concerns/.keep +0 -0
  129. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  130. data/spec/test_app/app/jobs/application_job.rb +2 -0
  131. data/spec/test_app/app/mailers/application_mailer.rb +4 -0
  132. data/spec/test_app/app/models/application_record.rb +3 -0
  133. data/spec/test_app/app/models/concerns/.keep +0 -0
  134. data/spec/test_app/app/views/components.rb +11 -0
  135. data/spec/test_app/app/views/components/hello_world.rb +11 -0
  136. data/spec/test_app/app/views/components/todo.rb +14 -0
  137. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  138. data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
  139. data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
  140. data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
  141. data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
  142. data/spec/test_app/bin/bundle +3 -0
  143. data/spec/test_app/bin/rails +4 -0
  144. data/spec/test_app/bin/rake +4 -0
  145. data/spec/test_app/bin/setup +38 -0
  146. data/spec/test_app/bin/update +29 -0
  147. data/spec/test_app/bin/yarn +11 -0
  148. data/spec/test_app/config.ru +5 -0
  149. data/spec/test_app/config/application.rb +45 -0
  150. data/spec/test_app/config/boot.rb +6 -0
  151. data/spec/test_app/config/cable.yml +10 -0
  152. data/spec/test_app/config/database.yml +25 -0
  153. data/spec/test_app/config/environment.rb +5 -0
  154. data/spec/test_app/config/environments/development.rb +54 -0
  155. data/spec/test_app/config/environments/production.rb +91 -0
  156. data/spec/test_app/config/environments/test.rb +42 -0
  157. data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
  158. data/spec/test_app/config/initializers/assets.rb +14 -0
  159. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  160. data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
  161. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  162. data/spec/test_app/config/initializers/inflections.rb +16 -0
  163. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  164. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  165. data/spec/test_app/config/locales/en.yml +33 -0
  166. data/spec/test_app/config/puma.rb +56 -0
  167. data/spec/test_app/config/routes.rb +3 -0
  168. data/spec/test_app/config/secrets.yml +32 -0
  169. data/spec/test_app/config/spring.rb +6 -0
  170. data/spec/test_app/db/development.sqlite3 +0 -0
  171. data/spec/test_app/db/schema.rb +15 -0
  172. data/spec/test_app/db/seeds.rb +7 -0
  173. data/spec/test_app/db/test.sqlite3 +0 -0
  174. data/spec/test_app/lib/assets/.keep +0 -0
  175. data/spec/test_app/log/.keep +0 -0
  176. data/spec/test_app/package.json +5 -0
  177. data/spec/test_app/public/404.html +67 -0
  178. data/spec/test_app/public/422.html +67 -0
  179. data/spec/test_app/public/500.html +66 -0
  180. data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
  181. data/spec/test_app/public/apple-touch-icon.png +0 -0
  182. data/spec/test_app/public/favicon.ico +0 -0
  183. data/spec/vendor/es5-shim.min.js +7 -0
  184. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  185. metadata +401 -61
  186. data/CODE_OF_CONDUCT.md +0 -49
  187. data/lib/react/version.rb +0 -3
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe 'React::TopLevelRailsComponent', js: true do
5
+ before :each do
6
+ on_client do
7
+ module Components
8
+ module Controller
9
+ class Component1
10
+ include React::Component
11
+ def render
12
+ self.class.name.to_s
13
+ end
14
+ end
15
+ end
16
+
17
+ class Component1
18
+ include React::Component
19
+ def render
20
+ self.class.name.to_s
21
+ end
22
+ end
23
+
24
+ class Component2
25
+ include React::Component
26
+ def render
27
+ self.class.name.to_s
28
+ end
29
+ end
30
+ end
31
+
32
+ module Controller
33
+ class SomeOtherClass # see issue #80
34
+ end
35
+ end
36
+
37
+ class Component1
38
+ include React::Component
39
+ def render
40
+ self.class.name.to_s
41
+ end
42
+ end
43
+
44
+ def render_top_level(controller, component_name)
45
+ params = {
46
+ controller: controller,
47
+ component_name: component_name,
48
+ render_params: {}
49
+ }
50
+ component = React::Test::Utils.render_component_into_document(React::TopLevelRailsComponent, params)
51
+ component.dom_node.JS[:outerHTML]
52
+ end
53
+ end
54
+ end
55
+
56
+ it 'uses the controller name to lookup a component' do
57
+ expect_evaluate_ruby('render_top_level("Controller", "Component1")').to eq('<span>Components::Controller::Component1</span>')
58
+ end
59
+
60
+ it 'can find the name without matching the controller' do
61
+ expect_evaluate_ruby('render_top_level("Controller", "Component2")').to eq('<span>Components::Component2</span>')
62
+ end
63
+
64
+ it 'will find the outer most matching component' do
65
+ expect_evaluate_ruby('render_top_level("OtherController", "Component1")').to eq('<span>Component1</span>')
66
+ end
67
+
68
+ it 'can find the correct component when the name is fully qualified' do
69
+ expect_evaluate_ruby('render_top_level("Controller", "::Components::Component1")').to eq('<span>Components::Component1</span>')
70
+ end
71
+
72
+ describe '.html_tag?' do
73
+ it 'is truthy for valid html tags' do
74
+ expect_evaluate_ruby('React.html_tag?("a")').to be_truthy
75
+ expect_evaluate_ruby('React.html_tag?("div")').to be_truthy
76
+ end
77
+
78
+ it 'is truthy for valid svg tags' do
79
+ expect_evaluate_ruby('React.html_tag?("svg")').to be_truthy
80
+ expect_evaluate_ruby('React.html_tag?("circle")').to be_truthy
81
+ end
82
+
83
+ it 'is falsey for invalid tags' do
84
+ expect_evaluate_ruby('React.html_tag?("tagizzle")').to be_falsey
85
+ end
86
+ end
87
+
88
+ describe '.html_attr?' do
89
+ it 'is truthy for valid html attributes' do
90
+ expect_evaluate_ruby('React.html_attr?("id")').to be_truthy
91
+ expect_evaluate_ruby('React.html_attr?("data")').to be_truthy
92
+ end
93
+
94
+ it 'is truthy for valid svg attributes' do
95
+ expect_evaluate_ruby('React.html_attr?("cx")').to be_truthy
96
+ expect_evaluate_ruby('React.html_attr?("strokeWidth")').to be_truthy
97
+ end
98
+
99
+ it 'is falsey for invalid attributes' do
100
+ expect_evaluate_ruby('React.html_tag?("attrizzle")').to be_falsey
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'An Example from the react.rb doc', js: true do
4
+ it 'produces the correct result' do
5
+ mount 'HelloMessage' do
6
+ class HelloMessage
7
+ include React::Component
8
+ def render
9
+ div { "Hello World!" }
10
+ end
11
+ end
12
+ end
13
+ expect(page).to have_xpath('//div', text: 'Hello World!')
14
+ end
15
+ end
16
+
17
+ describe 'Adding state to a component (second tutorial example)', js: true do
18
+ before :each do
19
+ on_client do
20
+ class HelloMessage2
21
+ include React::Component
22
+ define_state(:user_name) { '@catmando' }
23
+ def render
24
+ div { "Hello #{state.user_name}" }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ it "produces the correct result" do
31
+ mount 'HelloMessage2'
32
+ expect(page).to have_xpath('//div', text: 'Hello @catmando')
33
+ end
34
+
35
+ it 'renders to the document' do
36
+ evaluate_ruby do
37
+ ele = JS.call(:eval, "document.body.appendChild(document.createElement('div'))")
38
+ React.render(React.create_element(HelloMessage2), ele)
39
+ end
40
+ expect(page).to have_xpath('//div', text: 'Hello @catmando')
41
+ end
42
+ end
@@ -0,0 +1,134 @@
1
+ require "spec_helper"
2
+
3
+ describe 'React::Validator', js: true do
4
+ describe '#validate' do
5
+ describe "Presence validation" do
6
+ it "should check if required props provided" do
7
+ evaluate_ruby do
8
+ VALIDATOR = React::Validator.new.build do
9
+ requires :foo
10
+ requires :bar
11
+ end
12
+ end
13
+ expect_evaluate_ruby('VALIDATOR.validate({})').to eq(["Required prop `foo` was not specified", "Required prop `bar` was not specified"])
14
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 1, bar: 3})').to eq([])
15
+ end
16
+
17
+ it "should check if passed non specified prop" do
18
+ evaluate_ruby do
19
+ VALIDATOR = React::Validator.new.build do
20
+ optional :foo
21
+ end
22
+ end
23
+ expect_evaluate_ruby('VALIDATOR.validate({bar: 10})').to eq(["Provided prop `bar` not specified in spec"])
24
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 10})').to eq([])
25
+ end
26
+ end
27
+
28
+ describe "Type validation" do
29
+ it "should check if passed value with wrong type" do
30
+ evaluate_ruby do
31
+ VALIDATOR = React::Validator.new.build do
32
+ requires :foo, type: String
33
+ end
34
+ end
35
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 10})').to eq(["Provided prop `foo` could not be converted to String"])
36
+ expect_evaluate_ruby('VALIDATOR.validate({foo: "10"})').to eq([])
37
+ end
38
+
39
+ it "should check if passed value with wrong custom type" do
40
+ evaluate_ruby do
41
+ class Bar; end
42
+ VALIDATOR = React::Validator.new.build do
43
+ requires :foo, type: Bar
44
+ end
45
+ end
46
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 10})').to eq(["Provided prop `foo` could not be converted to Bar"])
47
+ expect_evaluate_ruby('VALIDATOR.validate({foo: Bar.new})').to eq([])
48
+ end
49
+
50
+ it 'coerces native JS prop types to opal objects' do
51
+ evaluate_ruby do
52
+ VALIDATOR = React::Validator.new.build do
53
+ requires :foo, type: JS.call(:eval, "(function () { return { x: 1 }; })();")
54
+ end
55
+ end
56
+ expect_evaluate_ruby('VALIDATOR.validate({foo: `{ x: 1 }`})').to eq(["Provided prop `foo` could not be converted to [object Object]"])
57
+ end
58
+
59
+ it 'coerces native JS values to opal objects' do
60
+ evaluate_ruby do
61
+ VALIDATOR = React::Validator.new.build do
62
+ requires :foo, type: Array[Integer]
63
+ end
64
+ end
65
+ expect_evaluate_ruby('VALIDATOR.validate({foo: `[ { x: 1 } ]`})').to eq(["Provided prop `foo`[0] could not be converted to #{Integer.name}"])
66
+ end
67
+
68
+ it "should support Array[Class] validation" do
69
+ evaluate_ruby do
70
+ VALIDATOR = React::Validator.new.build do
71
+ requires :foo, type: Array[Hash]
72
+ end
73
+ end
74
+ expect_evaluate_ruby('VALIDATOR.validate({foo: [1,"2",3]})').to eq(
75
+ [
76
+ "Provided prop `foo`[0] could not be converted to Hash",
77
+ "Provided prop `foo`[1] could not be converted to Hash",
78
+ "Provided prop `foo`[2] could not be converted to Hash"
79
+ ]
80
+ )
81
+ expect_evaluate_ruby('VALIDATOR.validate({foo: [{},{},{}]})').to eq([])
82
+ end
83
+ end
84
+
85
+ describe "Limited values" do
86
+ it "should check if passed value is not one of the specified values" do
87
+ evaluate_ruby do
88
+ VALIDATOR = React::Validator.new.build do
89
+ requires :foo, values: [4,5,6]
90
+ end
91
+ end
92
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 3})').to eq(["Value `3` for prop `foo` is not an allowed value"])
93
+ expect_evaluate_ruby('VALIDATOR.validate({foo: 4})').to eq([])
94
+ end
95
+ end
96
+ end
97
+
98
+ describe '#undefined_props' do
99
+ before :each do
100
+ on_client do
101
+ PROPS = { foo: 'foo', bar: 'bar', biz: 'biz', baz: 'baz' }
102
+ VALIDATOR = React::Validator.new.build do
103
+ requires :foo
104
+ optional :bar
105
+ end
106
+ end
107
+ end
108
+
109
+
110
+ it 'slurps up any extra params into a hash' do
111
+ expect_evaluate_ruby('VALIDATOR.undefined_props(PROPS)').to eq({ "biz" => 'biz', "baz" => 'baz' })
112
+ end
113
+
114
+ it 'prevents validate non-specified params' do
115
+ evaluate_ruby do
116
+ VALIDATOR.undefined_props(PROPS)
117
+ end
118
+ expect_evaluate_ruby('VALIDATOR.validate(PROPS)').to eq([])
119
+ end
120
+ end
121
+
122
+ describe "default_props" do
123
+ it "should return specified default values" do
124
+ evaluate_ruby do
125
+ VALIDATOR = React::Validator.new.build do
126
+ requires :foo, default: 10
127
+ requires :bar
128
+ optional :lorem, default: 20
129
+ end
130
+ end
131
+ expect_evaluate_ruby('VALIDATOR.default_props').to eq({"foo" => 10, "lorem" => 20})
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe ReactiveRuby::ComponentLoader do
4
+ GLOBAL_WRAPPER = <<-JS
5
+ #{React::ServerRendering::ExecJSRenderer::GLOBAL_WRAPPER}
6
+ var console = {
7
+ warn: function(s) { }
8
+ };
9
+ JS
10
+
11
+ let(:js) do
12
+ if ::Rails.application.assets['react-server.js']
13
+ react_source = ::Rails.application.assets['react-server.js']
14
+ else
15
+ react_source = ::Rails.application.assets['react.js']
16
+ end
17
+ ::Rails.application.assets['components'].to_s + react_source.to_s
18
+ end
19
+ let(:context) { ExecJS.compile(GLOBAL_WRAPPER + js) }
20
+ let(:v8_context) { ReactiveRuby::ServerRendering.context_instance_for(context) }
21
+
22
+ describe '.new' do
23
+ it 'raises a meaningful exception when initialized without a context' do
24
+ expect {
25
+ described_class.new(nil)
26
+ }.to raise_error(/Could not obtain ExecJS runtime context/)
27
+ end
28
+ end
29
+
30
+ describe '#load' do
31
+ xit 'loads given asset file into context' do
32
+ loader = described_class.new(v8_context)
33
+
34
+ expect {
35
+ loader.load('components')
36
+ }.to change { !!v8_context.eval('Opal.React !== undefined') }.from(false).to(true)
37
+ end
38
+
39
+ xit 'is truthy upon successful load' do
40
+ loader = described_class.new(v8_context)
41
+ expect(loader.load('components')).to be_truthy
42
+ end
43
+
44
+ xit 'fails silently returning false' do
45
+ loader = described_class.new(v8_context)
46
+ expect(loader.load('foo')).to be_falsey
47
+ end
48
+ end
49
+
50
+ describe '#load!' do
51
+ xit 'is truthy upon successful load' do
52
+ loader = described_class.new(v8_context)
53
+ expect(loader.load!('components')).to be_truthy
54
+ end
55
+
56
+ xit 'raises an expection if loading fails' do
57
+ loader = described_class.new(v8_context)
58
+ expect { loader.load!('foo') }.to raise_error(/No HyperReact components/)
59
+ end
60
+ end
61
+
62
+ describe '#loaded?' do
63
+ xit 'is truthy if components file is already loaded' do
64
+ loader = described_class.new(v8_context)
65
+ loader.load('components')
66
+ expect(loader).to be_loaded
67
+ end
68
+
69
+ xit 'is false if components file is not loaded' do
70
+ loader = described_class.new(v8_context)
71
+ expect(loader).to_not be_loaded
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe React::IsomorphicHelpers do
4
+ describe 'code execution context' do
5
+ let(:klass) { Class.send(:include, described_class) }
6
+
7
+ describe 'module class methods', :opal do
8
+ it { expect(described_class).to_not be_on_opal_server }
9
+ it { expect(described_class).to be_on_opal_client }
10
+ end
11
+
12
+ describe 'included class methods', :opal do
13
+ it { expect(klass).to_not be_on_opal_server }
14
+ it { expect(klass).to be_on_opal_client }
15
+ end
16
+
17
+ describe 'included instance methods', :opal do
18
+ it { expect(klass.new).to_not be_on_opal_server }
19
+ it { expect(klass.new).to be_on_opal_client }
20
+ end
21
+
22
+ describe 'module class methods', :ruby do
23
+ it { is_expected.to_not be_on_opal_server }
24
+ it { is_expected.to_not be_on_opal_client }
25
+ end
26
+
27
+ describe 'included class methods', :ruby do
28
+ subject { klass }
29
+ it { is_expected.to_not be_on_opal_server }
30
+ it { is_expected.to_not be_on_opal_client }
31
+ end
32
+
33
+ describe 'included instance methods', :ruby do
34
+ subject { klass.new }
35
+ it { is_expected.to_not be_on_opal_server }
36
+ it { is_expected.to_not be_on_opal_client }
37
+ end
38
+ end
39
+
40
+ describe 'load_context', :ruby do
41
+ let(:v8_context) { TestV8Context.new }
42
+ let(:controller) { double('controller') }
43
+ let(:name) { double('name') }
44
+
45
+ it 'creates a context and sets a controller' do
46
+ context = described_class.load_context(v8_context, controller, name)
47
+ expect(context.controller).to eq(controller)
48
+ end
49
+
50
+ it 'creates a context and sets a unique_id', js: true do
51
+ # this tests loads the prerender context and somehow trys evaluate_ruby, works only with above js: true
52
+ # TODO this is triggered by TimeCop for some reason
53
+ Timecop.freeze do
54
+ stamp = Time.now.to_i
55
+ context = described_class.load_context(v8_context, controller, name)
56
+ expect(context.unique_id).to eq("#{ controller.object_id }-#{ stamp }")
57
+ end
58
+ end
59
+ end
60
+
61
+ describe React::IsomorphicHelpers::Context do
62
+ class TestV8Context < Hash
63
+ def eval(args)
64
+ true
65
+ end
66
+ def attach(*args)
67
+ true
68
+ end
69
+ end
70
+
71
+ # Need to decouple/dry up this...
72
+ def test_context(files = nil)
73
+ js = ReactiveRuby::ServerRendering::ContextualRenderer::CONSOLE_POLYFILL.dup
74
+ js << Opal::Builder.build('opal').to_s
75
+ Array(files).each do |filename|
76
+ js << ::Rails.application.assets[filename].to_s
77
+ end
78
+ js = "#{React::ServerRendering::ExecJSRenderer::GLOBAL_WRAPPER}#{js}"
79
+ ctx = ExecJS.compile(js)
80
+ ctx = ReactiveRuby::ServerRendering.context_instance_for(ctx)
81
+ end
82
+
83
+ def react_context
84
+ if ::Rails.application.assets['react-server.js']
85
+ test_context(['server_rendering.js', 'react-server.js'])
86
+ else
87
+ test_context(['components', 'react.js'])
88
+ end
89
+ end
90
+
91
+ let(:v8_context) { TestV8Context.new }
92
+ let(:controller) { double('controller') }
93
+ let(:name) { double('name') }
94
+ before do
95
+ described_class.instance_variable_set :@before_first_mount_blocks, nil
96
+ end
97
+
98
+ describe '#initialize' do
99
+ it 'calls before mount callbacks' do
100
+ string = instance_double(String)
101
+ described_class.register_before_first_mount_block do
102
+ string.inspect
103
+ end
104
+ expect(string).to receive(:inspect).once
105
+ context = described_class.new('unique-id', v8_context, controller, name)
106
+ end
107
+ end
108
+
109
+ describe '#eval' do
110
+ it 'delegates to given context' do
111
+ context = described_class.new('unique-id', v8_context, controller, name)
112
+ js = 'true;'
113
+ expect(v8_context).to receive(:eval).with(js).once
114
+ context.eval(js)
115
+ end
116
+ end
117
+
118
+ describe '#send_to_opal' do
119
+ let(:opal_code) { Opal::Builder.new.build_str(ruby_code, __FILE__).to_s }
120
+ let(:ruby_code) { %Q[
121
+ module React::IsomorphicHelpers
122
+ def self.greet(name)
123
+ "Hello, " + name + "!"
124
+ end
125
+
126
+ def self.valediction
127
+ 'Goodbye'
128
+ end
129
+ end
130
+ ]}
131
+
132
+ it 'raises an error when react cannot be loaded' do
133
+ context = described_class.new('unique-id', v8_context, controller, name)
134
+ context.instance_variable_set(:@ctx, test_context)
135
+ expect {
136
+ context.send_to_opal(:foo)
137
+ }.to raise_error(/No HyperReact components found/)
138
+ end
139
+
140
+ it 'executes method with args inside opal rubyracer context' do
141
+ ctx = react_context
142
+ context = described_class.new('unique-id', ctx, controller, name)
143
+ context.eval(opal_code)
144
+ result = context.send_to_opal(:greet, 'world')
145
+ expect(result).to eq('Hello, world!')
146
+ end
147
+
148
+ it 'executes the method inside opal rubyracer context' do
149
+ ctx = react_context
150
+ context = described_class.new('unique-id', ctx, controller, name)
151
+ context.eval(opal_code)
152
+ result = context.send_to_opal(:valediction)
153
+ expect(result).to eq('Goodbye')
154
+ end
155
+ end
156
+ end
157
+ end