reactrb 0.8.8 → 0.9.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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +24 -3
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +1154 -3
  5. data/.travis.yml +20 -0
  6. data/Appraisals +20 -0
  7. data/CHANGELOG.md +28 -3
  8. data/Gemfile +4 -5
  9. data/README.md +6 -9
  10. data/Rakefile +6 -1
  11. data/config.ru +7 -6
  12. data/gemfiles/opal_0.8_react_13.gemfile +13 -0
  13. data/gemfiles/opal_0.8_react_14.gemfile +13 -0
  14. data/gemfiles/opal_0.8_react_15.gemfile +13 -0
  15. data/gemfiles/opal_0.9_react_13.gemfile +13 -0
  16. data/gemfiles/opal_0.9_react_14.gemfile +13 -0
  17. data/gemfiles/opal_0.9_react_15.gemfile +13 -0
  18. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +1 -1
  19. data/lib/rails-helpers/top_level_rails_component.rb +1 -1
  20. data/lib/react-sources/react-server.js +2 -0
  21. data/lib/react/api.rb +13 -12
  22. data/lib/react/children.rb +30 -0
  23. data/lib/react/component.rb +27 -46
  24. data/lib/react/component/class_methods.rb +28 -32
  25. data/lib/react/component/dsl_instance_methods.rb +4 -34
  26. data/lib/react/component/params.rb +6 -0
  27. data/lib/react/component/props_wrapper.rb +22 -27
  28. data/lib/react/component/should_component_update.rb +98 -0
  29. data/lib/react/component/tags.rb +45 -4
  30. data/lib/react/element.rb +26 -13
  31. data/lib/react/object.rb +15 -0
  32. data/lib/react/react-source.rb +9 -0
  33. data/lib/react/rendering_context.rb +97 -93
  34. data/lib/react/state.rb +27 -21
  35. data/lib/react/test.rb +16 -0
  36. data/lib/react/test/dsl.rb +17 -0
  37. data/lib/react/test/matchers/render_html_matcher.rb +49 -0
  38. data/lib/react/test/rspec.rb +15 -0
  39. data/lib/react/test/session.rb +46 -0
  40. data/lib/react/top_level.rb +50 -14
  41. data/lib/react/validator.rb +5 -5
  42. data/lib/reactive-ruby/isomorphic_helpers.rb +0 -7
  43. data/lib/reactive-ruby/version.rb +1 -1
  44. data/lib/reactrb.rb +14 -14
  45. data/lib/reactrb/deep-compare.rb +24 -0
  46. data/lib/sources/react-latest.js +2 -0
  47. data/lib/sources/react-v13.js +4 -1
  48. data/lib/sources/react-v14.js +3 -84
  49. data/lib/sources/react-v15.js +3 -0
  50. data/logo1.png +0 -0
  51. data/logo2.png +0 -0
  52. data/logo3.png +0 -0
  53. data/path_release_steps.md +1 -1
  54. data/reactrb.gemspec +2 -3
  55. data/spec/react/children_spec.rb +76 -0
  56. data/spec/react/component/base_spec.rb +3 -7
  57. data/spec/react/component_spec.rb +181 -60
  58. data/spec/react/dsl_spec.rb +26 -19
  59. data/spec/react/element_spec.rb +16 -1
  60. data/spec/react/native_library_spec.rb +20 -0
  61. data/spec/react/opal_jquery_extensions_spec.rb +27 -0
  62. data/spec/react/param_declaration_spec.rb +47 -78
  63. data/spec/react/react_spec.rb +7 -9
  64. data/spec/react/state_spec.rb +29 -0
  65. data/spec/react/test/dsl_spec.rb +43 -0
  66. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  67. data/spec/react/test/rspec_spec.rb +62 -0
  68. data/spec/react/test/session_spec.rb +100 -0
  69. data/spec/react/test/utils_spec.rb +45 -0
  70. data/spec/react/top_level_component_spec.rb +33 -5
  71. data/spec/react/tutorial/tutorial_spec.rb +5 -5
  72. data/spec/react/validator_spec.rb +10 -13
  73. data/spec/reactive-ruby/component_loader_spec.rb +3 -0
  74. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +5 -4
  75. data/spec/spec_helper.rb +6 -3
  76. data/spec/support/react/spec_helpers.rb +9 -2
  77. metadata +47 -124
  78. data/example/examples/Gemfile +0 -7
  79. data/example/examples/app/basics.js.rb +0 -42
  80. data/example/examples/app/items.rb +0 -11
  81. data/example/examples/app/jquery.js +0 -5
  82. data/example/examples/app/nodes.rb +0 -61
  83. data/example/examples/app/react-router.js +0 -6
  84. data/example/examples/app/react_api_demo.rb +0 -29
  85. data/example/examples/app/rerendering.rb +0 -72
  86. data/example/examples/app/reuse.rb +0 -59
  87. data/example/examples/app/show.rb +0 -52
  88. data/example/examples/config.ru +0 -38
  89. data/example/rails-tutorial/.gitignore +0 -17
  90. data/example/rails-tutorial/Gemfile +0 -51
  91. data/example/rails-tutorial/README.rdoc +0 -28
  92. data/example/rails-tutorial/Rakefile +0 -6
  93. data/example/rails-tutorial/app/assets/images/.keep +0 -0
  94. data/example/rails-tutorial/app/assets/javascripts/application.rb +0 -15
  95. data/example/rails-tutorial/app/assets/stylesheets/application.css +0 -15
  96. data/example/rails-tutorial/app/controllers/application_controller.rb +0 -6
  97. data/example/rails-tutorial/app/controllers/concerns/.keep +0 -0
  98. data/example/rails-tutorial/app/controllers/home_controller.rb +0 -6
  99. data/example/rails-tutorial/app/helpers/application_helper.rb +0 -2
  100. data/example/rails-tutorial/app/mailers/.keep +0 -0
  101. data/example/rails-tutorial/app/models/.keep +0 -0
  102. data/example/rails-tutorial/app/models/concerns/.keep +0 -0
  103. data/example/rails-tutorial/app/views/components.rb +0 -3
  104. data/example/rails-tutorial/app/views/components/home/show.rb +0 -47
  105. data/example/rails-tutorial/app/views/layouts/application.html.erb +0 -14
  106. data/example/rails-tutorial/bin/bundle +0 -3
  107. data/example/rails-tutorial/bin/rails +0 -8
  108. data/example/rails-tutorial/bin/rake +0 -8
  109. data/example/rails-tutorial/bin/setup +0 -29
  110. data/example/rails-tutorial/bin/spring +0 -15
  111. data/example/rails-tutorial/config.ru +0 -4
  112. data/example/rails-tutorial/config/application.rb +0 -26
  113. data/example/rails-tutorial/config/boot.rb +0 -3
  114. data/example/rails-tutorial/config/database.yml +0 -25
  115. data/example/rails-tutorial/config/environment.rb +0 -5
  116. data/example/rails-tutorial/config/environments/development.rb +0 -41
  117. data/example/rails-tutorial/config/environments/production.rb +0 -79
  118. data/example/rails-tutorial/config/environments/test.rb +0 -42
  119. data/example/rails-tutorial/config/initializers/assets.rb +0 -11
  120. data/example/rails-tutorial/config/initializers/backtrace_silencers.rb +0 -7
  121. data/example/rails-tutorial/config/initializers/cookies_serializer.rb +0 -3
  122. data/example/rails-tutorial/config/initializers/filter_parameter_logging.rb +0 -4
  123. data/example/rails-tutorial/config/initializers/inflections.rb +0 -16
  124. data/example/rails-tutorial/config/initializers/mime_types.rb +0 -4
  125. data/example/rails-tutorial/config/initializers/session_store.rb +0 -3
  126. data/example/rails-tutorial/config/initializers/wrap_parameters.rb +0 -14
  127. data/example/rails-tutorial/config/locales/en.yml +0 -23
  128. data/example/rails-tutorial/config/routes.rb +0 -59
  129. data/example/rails-tutorial/config/secrets.yml +0 -22
  130. data/example/rails-tutorial/db/seeds.rb +0 -7
  131. data/example/rails-tutorial/lib/assets/.keep +0 -0
  132. data/example/rails-tutorial/lib/tasks/.keep +0 -0
  133. data/example/rails-tutorial/log/.keep +0 -0
  134. data/example/rails-tutorial/public/404.html +0 -67
  135. data/example/rails-tutorial/public/422.html +0 -67
  136. data/example/rails-tutorial/public/500.html +0 -66
  137. data/example/rails-tutorial/public/favicon.ico +0 -0
  138. data/example/rails-tutorial/public/robots.txt +0 -5
  139. data/example/rails-tutorial/test/controllers/.keep +0 -0
  140. data/example/rails-tutorial/test/fixtures/.keep +0 -0
  141. data/example/rails-tutorial/test/helpers/.keep +0 -0
  142. data/example/rails-tutorial/test/integration/.keep +0 -0
  143. data/example/rails-tutorial/test/mailers/.keep +0 -0
  144. data/example/rails-tutorial/test/models/.keep +0 -0
  145. data/example/rails-tutorial/test/test_helper.rb +0 -10
  146. data/example/rails-tutorial/vendor/assets/javascripts/.keep +0 -0
  147. data/example/rails-tutorial/vendor/assets/stylesheets/.keep +0 -0
  148. data/example/sinatra-tutorial/.DS_Store +0 -0
  149. data/example/sinatra-tutorial/Gemfile +0 -5
  150. data/example/sinatra-tutorial/README.md +0 -8
  151. data/example/sinatra-tutorial/_comments.json +0 -42
  152. data/example/sinatra-tutorial/app/example.rb +0 -290
  153. data/example/sinatra-tutorial/app/jquery.js +0 -5
  154. data/example/sinatra-tutorial/config.ru +0 -58
  155. data/example/sinatra-tutorial/public/base.css +0 -62
  156. data/example/todos/Gemfile +0 -11
  157. data/example/todos/README.md +0 -37
  158. data/example/todos/Rakefile +0 -8
  159. data/example/todos/app/application.rb +0 -22
  160. data/example/todos/app/components/app.react.rb +0 -61
  161. data/example/todos/app/components/footer.react.rb +0 -31
  162. data/example/todos/app/components/todo_item.react.rb +0 -46
  163. data/example/todos/app/components/todo_list.react.rb +0 -25
  164. data/example/todos/app/models/todo.rb +0 -19
  165. data/example/todos/config.ru +0 -14
  166. data/example/todos/index.html.haml +0 -16
  167. data/example/todos/spec/todo_spec.rb +0 -28
  168. data/example/todos/vendor/base.css +0 -410
  169. data/example/todos/vendor/bg.png +0 -0
  170. data/example/todos/vendor/jquery.js +0 -4
@@ -19,6 +19,20 @@ describe React::Element do
19
19
  end
20
20
  end
21
21
 
22
+ describe "Event Subscription" do
23
+ it "keeps the original params" do
24
+ stub_const 'Foo', Class.new
25
+ Foo.class_eval do
26
+ include React::Component
27
+ def render
28
+ INPUT(value: nil, type: 'text').on(:change) {}
29
+ end
30
+ end
31
+
32
+ expect(React.render_to_static_markup(React.create_element(Foo))).to match(/<input (type="text" value=""|value="" type="text")(\/)?>/)
33
+ end
34
+ end
35
+
22
36
  describe 'Component Event Subscription' do
23
37
 
24
38
  it 'will subscribe to a component event param' do
@@ -87,7 +101,8 @@ describe React::Element do
87
101
  }
88
102
  expect(React.render_to_static_markup(React.create_element(Foo).on(:event) {'works!'})).to eq('<span>works!</span>')
89
103
  `window.console.warn = org_warn_console; window.console.error = org_error_console;`
90
- expect(`log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `on_event` not specified in spec", "Warning: Deprecated feature used in React::Component. In future releases React::Element#on('event') will no longer respond to the '_onEvent' emitter.\nRename your emitter param to 'on_event' or use .on('<_onEvent>')"])
104
+ expect(`log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `on_event` not specified in spec/)
105
+ expect(`log[1]`).to eq("Warning: Deprecated feature used in React::Component. In future releases React::Element#on('event') will no longer respond to the '_onEvent' emitter.\nRename your emitter param to 'on_event' or use .on('<_onEvent>')")
91
106
  end
92
107
  end
93
108
 
@@ -65,6 +65,26 @@ describe "React::NativeLibrary" do
65
65
  React.create_element(Foo::NativeComponent, name: "There"))).to eq('<div>Hello There</div>')
66
66
  end
67
67
 
68
+ it "will import a nested React.js library into the Ruby name space" do
69
+ %x{
70
+ window.NativeLibrary = {
71
+ NestedLibrary: {
72
+ NativeComponent: React.createClass({
73
+ displayName: "HelloMessage",
74
+ render: function render() {
75
+ return React.createElement("div", null, "Hello ", this.props.name);
76
+ }
77
+ })}
78
+ }
79
+ }
80
+ stub_const 'Foo', Class.new(React::NativeLibrary)
81
+ Foo.class_eval do
82
+ imports "NativeLibrary"
83
+ end
84
+ expect(React.render_to_static_markup(
85
+ React.create_element(Foo::NestedLibrary::NativeComponent, name: "There"))).to eq('<div>Hello There</div>')
86
+ end
87
+
68
88
  it "will rename an imported a React.js component" do
69
89
  %x{
70
90
  window.NativeLibrary = {
@@ -6,6 +6,27 @@ describe 'Element' do
6
6
  React::API.clear_component_class_cache
7
7
  end
8
8
 
9
+ it 'will reuse the wrapper componet class for the same Element' do
10
+ stub_const 'Foo', Class.new(React::Component::Base)
11
+ Foo.class_eval do
12
+ param :name
13
+ def render
14
+ "hello #{params.name}"
15
+ end
16
+
17
+ def component_will_unmount
18
+
19
+ end
20
+ end
21
+
22
+ expect_any_instance_of(Foo).to_not receive(:component_will_unmount)
23
+
24
+ test_div = Element.new(:div)
25
+ test_div.render { Foo(name: 'fred') }
26
+ test_div.render { Foo(name: 'freddy') }
27
+ expect(Element[test_div].find('span').html).to eq('hello freddy')
28
+ end
29
+
9
30
  it 'renders a top level component using render with a block' do
10
31
  stub_const 'Foo', Class.new(React::Component::Base)
11
32
  Foo.class_eval do
@@ -35,5 +56,11 @@ describe 'Element' do
35
56
 
36
57
  expect(Element[renderToDocument(Foo)].html).to eq('hello')
37
58
  end
59
+
60
+ it "accepts plain js object as selector" do
61
+ expect {
62
+ Element[`window`]
63
+ }.not_to raise_error
64
+ end
38
65
  end
39
66
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  if opal?
4
- describe 'the param macro' do
4
+ describe 'the param macro', type: :component do
5
5
  it 'defines collect_other_params_as method on params proxy' do
6
6
  stub_const 'Foo', Class.new(React::Component::Base)
7
7
  Foo.class_eval do
@@ -12,36 +12,7 @@ describe 'the param macro' do
12
12
  end
13
13
  end
14
14
 
15
- html = React.render_to_static_markup(React.create_element(Foo, { bar: 'biz' }))
16
- expect(html).to eq('<div>biz</div>')
17
- end
18
-
19
- it 'still defines deprecated collect_other_params_as method' do
20
- stub_const 'Foo', Class.new(React::Component::Base)
21
- Foo.class_eval do
22
- collect_other_params_as :foo
23
-
24
- def render
25
- div { foo[:bar] }
26
- end
27
- end
28
-
29
- html = React.render_to_static_markup(React.create_element(Foo, { bar: 'biz' }))
30
- expect(html).to eq('<div>biz</div>')
31
- end
32
-
33
- it 'still defines deprecated param accessor method' do
34
- stub_const 'Foo', Class.new(React::Component::Base)
35
- Foo.class_eval do
36
- param :foo
37
-
38
- def render
39
- div { foo }
40
- end
41
- end
42
-
43
- html = React.render_to_static_markup(React.create_element(Foo, {foo: :bar}))
44
- expect(html).to eq('<div>bar</div>')
15
+ expect(Foo).to render('<div>biz</div>').with_params(bar: 'biz')
45
16
  end
46
17
 
47
18
  it "can create and access a required param" do
@@ -54,7 +25,7 @@ describe 'the param macro' do
54
25
  end
55
26
  end
56
27
 
57
- expect(React.render_to_static_markup(React.create_element(Foo, {foo: :bar}))).to eq('<div>bar</div>')
28
+ expect(Foo).to render('<div>bar</div>').with_params(foo: :bar)
58
29
  end
59
30
 
60
31
  it "can create and access an optional params" do
@@ -71,7 +42,7 @@ describe 'the param macro' do
71
42
  end
72
43
  end
73
44
 
74
- expect(React.render_to_static_markup(React.create_element(Foo, {foo1: :bar1, foo3: :bar3}))).to eq('<div>bar1-no_bar2-bar3-no_bar4</div>')
45
+ expect(Foo).to render('<div>bar1-no_bar2-bar3-no_bar4</div>').with_params(foo1: :bar1, foo3: :bar3)
75
46
  end
76
47
 
77
48
  it 'can specify validation rules with the type option' do
@@ -95,7 +66,7 @@ describe 'the param macro' do
95
66
  end
96
67
  end
97
68
 
98
- expect(React.render_to_static_markup(React.create_element(Foo, {foo1: 12, foo2: "string"}))).to eq('<div>12-string</div>')
69
+ expect(Foo).to render('<div>12-string</div>').with_params(foo1: 12, foo2: "string")
99
70
  end
100
71
 
101
72
  it 'logs error in warning if validation failed' do
@@ -117,7 +88,7 @@ describe 'the param macro' do
117
88
  renderToDocument(Foo2, bar: 10, lorem: Lorem.new)
118
89
  `window.console.warn = org_warn_console; window.console.error = org_error_console;`
119
90
 
120
- expect(`log`).to eq(["Warning: Failed propType: In component `Foo2`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String"])
91
+ expect(`log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo2`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String/)
121
92
  end
122
93
 
123
94
  it 'should not log anything if validation passes' do
@@ -163,7 +134,7 @@ describe 'the param macro' do
163
134
  param :bar, type: []
164
135
  end
165
136
  renderToDocument(Foo, foo: 10, bar: [10])
166
- expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to Array"])
137
+ expect(`window.dummy_log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to Array/)
167
138
  end
168
139
 
169
140
  it "can use the [xxx] notation for arrays of a specific type" do
@@ -172,7 +143,7 @@ describe 'the param macro' do
172
143
  param :bar, type: [String]
173
144
  end
174
145
  renderToDocument(Foo, foo: [10], bar: ["10"])
175
- expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo`[0] could not be converted to String"])
146
+ expect(`window.dummy_log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo`\[0\] could not be converted to String/)
176
147
  end
177
148
 
178
149
  it "can convert a json hash to a type" do
@@ -194,13 +165,13 @@ describe 'the param macro' do
194
165
  "#{params.bar.kind}, #{params.baz[0].kind}"
195
166
  end
196
167
  end
197
- expect(React.render_to_static_markup(React.create_element(
198
- Foo, foo: "", bar: {bazwoggle: 1}, baz: [{bazwoggle: 2}]))).to eq('<span>1, 2</span>')
199
- expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle"])
168
+
169
+ params = { foo: "", bar: { bazwoggle: 1 }, baz: [{ bazwoggle: 2 }] }
170
+ expect(Foo).to render('<span>1, 2</span>').with_params(params)
171
+ expect(`window.dummy_log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle/)
200
172
  end
201
173
 
202
174
  describe "converts params only once" do
203
-
204
175
  it "not on every access" do
205
176
  stub_const "BazWoggle", Class.new
206
177
  BazWoggle.class_eval do
@@ -222,41 +193,39 @@ describe 'the param macro' do
222
193
  expect(React.render_to_static_markup(React.create_element(Foo, foo: {bazwoggle: 1}))).to eq('<span>2</span>')
223
194
  end
224
195
 
225
- # bug fixes in 0.9 render this test obsolete (hopefully)
226
-
227
- # it "even if contains an embedded native object" do
228
- # stub_const "Bar", Class.new(React::Component::Base)
229
- # stub_const "BazWoggle", Class.new
230
- # BazWoggle.class_eval do
231
- # def initialize(kind)
232
- # @kind = kind
233
- # end
234
- # attr_accessor :kind
235
- # def self._react_param_conversion(json, validate_only)
236
- # new(JSON.from_object(json[0])[:bazwoggle]) if JSON.from_object(json[0])[:bazwoggle]
237
- # end
238
- # end
239
- # Bar.class_eval do
240
- # param :foo, type: BazWoggle
241
- # def render
242
- # params.foo.kind.to_s
243
- # end
244
- # end
245
- # Foo.class_eval do
246
- # export_state :change_me
247
- # before_mount do
248
- # Foo.change_me! "initial"
249
- # end
250
- # def render
251
- # foo = [`{bazwoggle: #{Foo.change_me}}`]
252
- # Bar(foo: foo)
253
- # end
254
- # end
255
- # div = `document.createElement("div")`
256
- # React.render(React.create_element(Foo, {}), div)
257
- # Foo.change_me! "updated"
258
- # expect(`div.children[0].innerHTML`).to eq("updated")
259
- # end
196
+ it "even if contains an embedded native object" do
197
+ pending 'Fix after merging'
198
+ stub_const "Bar", Class.new(React::Component::Base)
199
+ stub_const "BazWoggle", Class.new
200
+ BazWoggle.class_eval do
201
+ def initialize(kind)
202
+ @kind = kind
203
+ end
204
+ attr_accessor :kind
205
+ def self._react_param_conversion(json, validate_only)
206
+ new(JSON.from_object(json[0])[:bazwoggle]) if JSON.from_object(json[0])[:bazwoggle]
207
+ end
208
+ end
209
+ Bar.class_eval do
210
+ param :foo, type: BazWoggle
211
+ def render
212
+ params.foo.kind.to_s
213
+ end
214
+ end
215
+ Foo.class_eval do
216
+ export_state :change_me
217
+ before_mount do
218
+ Foo.change_me! "initial"
219
+ end
220
+ def render
221
+ Bar(foo: Native([`{bazwoggle: #{Foo.change_me}}`]))
222
+ end
223
+ end
224
+ div = `document.createElement("div")`
225
+ React.render(React.create_element(Foo, {}), div)
226
+ Foo.change_me! "updated"
227
+ expect(`div.children[0].innerHTML`).to eq("updated")
228
+ end
260
229
  end
261
230
 
262
231
  it "will alias a Proc type param" do
@@ -266,7 +235,7 @@ describe 'the param macro' do
266
235
  params.foo
267
236
  end
268
237
  end
269
- expect(React.render_to_static_markup(React.create_element(Foo, foo: lambda { 'works!' }))).to eq('<span>works!</span>')
238
+ expect(Foo).to render('<span>works!</span>').with_params(foo: lambda { 'works!' })
270
239
  end
271
240
 
272
241
  it "will create a 'bang' (i.e. update) method if the type is React::Observable" do
@@ -281,7 +250,7 @@ describe 'the param macro' do
281
250
  end
282
251
  current_state = ""
283
252
  observer = React::Observable.new(current_state) { |new_state| current_state = new_state }
284
- expect(React.render_to_static_markup(React.create_element(Foo, foo: observer))).to eq('<span>ha!</span>')
253
+ expect(Foo).to render('<span>ha!</span>').with_params(foo: observer)
285
254
  expect(current_state).to eq("ha!")
286
255
  end
287
256
  end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  if opal?
4
- describe React do
4
+ RSpec.describe React, type: :component do
5
5
  after(:each) do
6
6
  React::API.clear_component_class_cache
7
7
  end
@@ -102,7 +102,7 @@ describe React do
102
102
  end
103
103
  end
104
104
 
105
- expect(React.render_to_static_markup(React.create_element(Foo))).to eq("<div>20</div>")
105
+ expect(Foo).to render("<div>20</div>")
106
106
  end
107
107
 
108
108
  it "should match the instance cycle to ReactComponent life cycle" do
@@ -142,13 +142,11 @@ describe React do
142
142
  end
143
143
 
144
144
  describe "class_name helpers (React.addons.classSet)" do
145
-
146
- # deprecated as of React 14
147
- # it "should transform Hash provided to `class_name` props as string" do
148
- # classes = {foo: true, bar: false, lorem: true}
149
- # element = React.create_element("div", class_name: classes)
150
- # expect(element.props.className).to eq("foo lorem")
151
- # end
145
+ it "should transform Hash provided to `class_name` props as string", v13_only: true do
146
+ classes = {foo: true, bar: false, lorem: true}
147
+ element = React.create_element("div", class_name: classes)
148
+ expect(element.props.className).to eq("foo lorem")
149
+ end
152
150
 
153
151
  it "should not alter behavior when passing a string" do
154
152
  element = React.create_element("div", class_name: "foo bar")
@@ -22,5 +22,34 @@ describe 'React::State' do
22
22
  # React::State.set_state(object, attribute, value) +
23
23
  # React::State.get_state(object, attribute)
24
24
  it "can be accessed outside of react using get/set_state"
25
+
26
+ it 'ignores state updates during rendering' do
27
+ stub_const 'StateTest', Class.new(React::Component::Base)
28
+ StateTest.class_eval do
29
+ export_state :boom
30
+ before_mount do
31
+ # force boom to be on the observing list during the current rendering cycle
32
+ StateTest.boom! !StateTest.boom
33
+ # this is automatically called by after_mount / after_update, but we don't want
34
+ # to have to setup a complicated async test, so we just force it now.
35
+ # if we don't do this, then updating boom will have no effect on the first render
36
+ React::State.update_states_to_observe
37
+ end
38
+ def render
39
+ (StateTest.boom ? "Boom" : "No Boom").tap { StateTest.boom! !StateTest.boom }
40
+ end
41
+ end
42
+ %x{
43
+ var log = [];
44
+ var org_warn_console = window.console.warn;
45
+ var org_error_console = window.console.error;
46
+ window.console.warn = window.console.error = function(str){log.push(str)}
47
+ }
48
+ markup = React.render_to_static_markup(React.create_element(StateTest))
49
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
50
+ expect(markup).to eq('<span>Boom</span>')
51
+ expect(StateTest.boom).to be_falsy
52
+ expect(`log`).to eq([])
53
+ end
25
54
  end
26
55
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ RSpec.describe React::Test::DSL do
5
+ describe 'the DSL' do
6
+ let(:session) { Class.new { include React::Test::DSL }.new }
7
+
8
+ before do
9
+ React::Test.reset_session!
10
+
11
+ stub_const 'Greeter', Class.new
12
+ Greeter.class_eval do
13
+ include React::Component
14
+
15
+ params do
16
+ optional :message
17
+ optional :from
18
+ end
19
+
20
+ def render
21
+ span { "Hello #{params.message}" }
22
+ end
23
+ end
24
+ end
25
+
26
+ it 'is possible to include it in another class' do
27
+ session.mount(Greeter)
28
+ expect(session.instance).to be_a(Greeter)
29
+ end
30
+
31
+ it "should provide a 'component' shortcut for more expressive tests" do
32
+ session.component.mount(Greeter)
33
+ expect(session.component.instance).to be_a(Greeter)
34
+ end
35
+
36
+ React::Test::Session::DSL_METHODS.each do |method|
37
+ it "responds to all DSL method: #{method}" do
38
+ expect(session).to respond_to(method)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Test::Matchers::RenderHTMLMatcher do
5
+ let(:component) {
6
+ Class.new do
7
+ include React::Component
8
+ params do
9
+ optional :string
10
+ end
11
+ def render
12
+ div do
13
+ span { params.string } if params.string
14
+ 'lorem'
15
+ end
16
+ end
17
+ end
18
+ }
19
+ let(:expected) { '<div>lorem</div>' }
20
+ let(:matcher) { described_class.new(expected) }
21
+
22
+ describe '#matches?' do
23
+ it 'is truthy when rendered component html equals expected html' do
24
+ expect(matcher.matches?(component)).to be_truthy
25
+ end
26
+
27
+ it 'is falsey when rendered component html does not equal expected html' do
28
+ matcher = described_class.new('foo')
29
+ expect(matcher.matches?(component)).to be_falsey
30
+ end
31
+ end
32
+
33
+ describe '#with_params' do
34
+ let(:expected) { '<div><span>str</span>lorem</div>' }
35
+
36
+ it 'renders the component with the given params' do
37
+ matcher.with_params(string: 'str')
38
+ expect(matcher.matches?(component)).to be_truthy
39
+ end
40
+ end
41
+
42
+ describe '#failure_message' do
43
+ let(:expected) { '<div><span>str</span>lorem</div>' }
44
+
45
+ it 'includes the name of the component' do
46
+ stub_const 'Foo', component
47
+ matcher.matches?(Foo)
48
+ expect(matcher.failure_message).to match(/expected 'Foo'/)
49
+ end
50
+
51
+ it 'includes the params hash' do
52
+ matcher.with_params(string: 'bar')
53
+ matcher.matches?(component)
54
+ expect(matcher.failure_message).to match(/with params '{"string"=>"bar"}'/)
55
+ end
56
+
57
+ it 'includes the expected html value' do
58
+ matcher.matches?(component)
59
+ expect(matcher.failure_message).to match(/to render '#{expected}'/)
60
+ end
61
+
62
+ it 'includes the actual html value' do
63
+ actual = '<div>lorem<\/div>'
64
+ matcher.matches?(component)
65
+ expect(matcher.failure_message).to match(/, but '#{actual}' was rendered/)
66
+ end
67
+
68
+ it 'does not include "to not render"' do
69
+ matcher.matches?(component)
70
+ expect(matcher.failure_message).to_not match(/to not render/)
71
+ end
72
+ end
73
+
74
+ describe '#negative_failure_message' do
75
+ let(:expected) { '<div><span>str</span>lorem</div>' }
76
+
77
+ it 'includes "to not render"' do
78
+ matcher.matches?(component)
79
+ expect(matcher.negative_failure_message).to match(/to not render/)
80
+ end
81
+ end
82
+ end
83
+ end