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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +27 -0
- data/.gitignore +30 -37
- data/.rubocop.yml +1159 -0
- data/.travis.yml +32 -0
- data/Appraisals +31 -0
- data/CHANGELOG.md +143 -0
- data/DOCS.md +1515 -0
- data/Gemfile +2 -5
- data/LICENSE +19 -0
- data/README.md +5 -33
- data/Rakefile +25 -6
- data/UPGRADING.md +24 -0
- data/component-name-lookup.md +145 -0
- data/dciy.toml +3 -0
- data/dciy_prepare.sh +8 -0
- data/dciy_run.sh +10 -0
- data/hyper-react.gemspec +24 -18
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
- data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
- data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
- data/lib/hyper-react.rb +66 -4
- data/lib/rails-helpers/top_level_rails_component.rb +75 -0
- data/lib/react/api.rb +203 -0
- data/lib/react/callbacks.rb +41 -0
- data/lib/react/children.rb +30 -0
- data/lib/react/component.rb +177 -0
- data/lib/react/component/api.rb +69 -0
- data/lib/react/component/base.rb +13 -0
- data/lib/react/component/class_methods.rb +181 -0
- data/lib/react/component/dsl_instance_methods.rb +23 -0
- data/lib/react/component/params.rb +6 -0
- data/lib/react/component/props_wrapper.rb +78 -0
- data/lib/react/component/should_component_update.rb +99 -0
- data/lib/react/component/tags.rb +108 -0
- data/lib/react/config.rb +5 -0
- data/lib/react/config/client.rb.erb +19 -0
- data/lib/react/config/server.rb +23 -0
- data/lib/react/element.rb +150 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/opal-jquery/element.rb +26 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/hash.rb +13 -0
- data/lib/react/native_library.rb +87 -0
- data/lib/react/object.rb +15 -0
- data/lib/react/react-source-browser.rb +3 -0
- data/lib/react/react-source-server.rb +3 -0
- data/lib/react/react-source.rb +16 -0
- data/lib/react/ref_callback.rb +31 -0
- data/lib/react/rendering_context.rb +146 -0
- data/lib/react/server.rb +19 -0
- data/lib/react/state_wrapper.rb +23 -0
- data/lib/react/test.rb +16 -0
- data/lib/react/test/dsl.rb +17 -0
- data/lib/react/test/matchers/render_html_matcher.rb +56 -0
- data/lib/react/test/rspec.rb +15 -0
- data/lib/react/test/session.rb +37 -0
- data/lib/react/test/utils.rb +71 -0
- data/lib/react/top_level.rb +110 -0
- data/lib/react/top_level_render.rb +28 -0
- data/lib/react/validator.rb +136 -0
- data/lib/reactive-ruby/component_loader.rb +43 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +235 -0
- data/lib/reactive-ruby/rails.rb +8 -0
- data/lib/reactive-ruby/rails/component_mount.rb +48 -0
- data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
- data/lib/reactive-ruby/rails/railtie.rb +20 -0
- data/lib/reactive-ruby/serializers.rb +15 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/lib/reactrb/auto-import.rb +27 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- data/spec/controller_helper_spec.rb +35 -0
- data/spec/index.html.erb +11 -0
- data/spec/react/callbacks_spec.rb +142 -0
- data/spec/react/children_spec.rb +132 -0
- data/spec/react/component/base_spec.rb +36 -0
- data/spec/react/component_spec.rb +1073 -0
- data/spec/react/dsl_spec.rb +323 -0
- data/spec/react/element_spec.rb +132 -0
- data/spec/react/event_spec.rb +39 -0
- data/spec/react/native_library_spec.rb +387 -0
- data/spec/react/observable_spec.rb +31 -0
- data/spec/react/opal_jquery_extensions_spec.rb +68 -0
- data/spec/react/param_declaration_spec.rb +253 -0
- data/spec/react/react_spec.rb +278 -0
- data/spec/react/refs_callback_spec.rb +65 -0
- data/spec/react/server_spec.rb +25 -0
- data/spec/react/state_spec.rb +52 -0
- data/spec/react/test/dsl_spec.rb +43 -0
- data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
- data/spec/react/test/rspec_spec.rb +62 -0
- data/spec/react/test/session_spec.rb +88 -0
- data/spec/react/test/utils_spec.rb +28 -0
- data/spec/react/top_level_component_spec.rb +103 -0
- data/spec/react/tutorial/tutorial_spec.rb +42 -0
- data/spec/react/validator_spec.rb +134 -0
- data/spec/reactive-ruby/component_loader_spec.rb +74 -0
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
- data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
- data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/test_app/README.md +24 -0
- data/spec/test_app/Rakefile +6 -0
- data/spec/test_app/app/assets/config/manifest.js +3 -0
- data/spec/test_app/app/assets/images/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/application.rb +7 -0
- data/spec/test_app/app/assets/javascripts/cable.js +13 -0
- data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
- data/spec/test_app/app/assets/stylesheets/application.css +15 -0
- data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
- data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
- data/spec/test_app/app/controllers/application_controller.rb +3 -0
- data/spec/test_app/app/controllers/concerns/.keep +0 -0
- data/spec/test_app/app/helpers/application_helper.rb +2 -0
- data/spec/test_app/app/jobs/application_job.rb +2 -0
- data/spec/test_app/app/mailers/application_mailer.rb +4 -0
- data/spec/test_app/app/models/application_record.rb +3 -0
- data/spec/test_app/app/models/concerns/.keep +0 -0
- data/spec/test_app/app/views/components.rb +11 -0
- data/spec/test_app/app/views/components/hello_world.rb +11 -0
- data/spec/test_app/app/views/components/todo.rb +14 -0
- data/spec/test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
- data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
- data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
- data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
- data/spec/test_app/bin/bundle +3 -0
- data/spec/test_app/bin/rails +4 -0
- data/spec/test_app/bin/rake +4 -0
- data/spec/test_app/bin/setup +38 -0
- data/spec/test_app/bin/update +29 -0
- data/spec/test_app/bin/yarn +11 -0
- data/spec/test_app/config.ru +5 -0
- data/spec/test_app/config/application.rb +45 -0
- data/spec/test_app/config/boot.rb +6 -0
- data/spec/test_app/config/cable.yml +10 -0
- data/spec/test_app/config/database.yml +25 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +54 -0
- data/spec/test_app/config/environments/production.rb +91 -0
- data/spec/test_app/config/environments/test.rb +42 -0
- data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/test_app/config/initializers/assets.rb +14 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
- data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/test_app/config/initializers/inflections.rb +16 -0
- data/spec/test_app/config/initializers/mime_types.rb +4 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/locales/en.yml +33 -0
- data/spec/test_app/config/puma.rb +56 -0
- data/spec/test_app/config/routes.rb +3 -0
- data/spec/test_app/config/secrets.yml +32 -0
- data/spec/test_app/config/spring.rb +6 -0
- data/spec/test_app/db/development.sqlite3 +0 -0
- data/spec/test_app/db/schema.rb +15 -0
- data/spec/test_app/db/seeds.rb +7 -0
- data/spec/test_app/db/test.sqlite3 +0 -0
- data/spec/test_app/lib/assets/.keep +0 -0
- data/spec/test_app/log/.keep +0 -0
- data/spec/test_app/package.json +5 -0
- data/spec/test_app/public/404.html +67 -0
- data/spec/test_app/public/422.html +67 -0
- data/spec/test_app/public/500.html +66 -0
- data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/test_app/public/apple-touch-icon.png +0 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/vendor/es5-shim.min.js +7 -0
- data/spec/vendor/jquery-2.2.4.min.js +4 -0
- metadata +401 -61
- data/CODE_OF_CONDUCT.md +0 -49
- data/lib/react/version.rb +0 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# rubocop:disable Style/FileName
|
|
2
|
+
# require 'reactrb/auto-import' to automatically
|
|
3
|
+
# import JS libraries and components when they are detected
|
|
4
|
+
if RUBY_ENGINE == 'opal'
|
|
5
|
+
# modifies const and method_missing so that they will attempt
|
|
6
|
+
# to auto import native libraries and components using React::NativeLibrary
|
|
7
|
+
class Object
|
|
8
|
+
class << self
|
|
9
|
+
alias _reactrb_original_const_missing const_missing
|
|
10
|
+
alias _reactrb_original_method_missing method_missing
|
|
11
|
+
|
|
12
|
+
def const_missing(const_name)
|
|
13
|
+
# Opal uses const_missing to initially define things,
|
|
14
|
+
# so we always call the original, and respond to the exception
|
|
15
|
+
_reactrb_original_const_missing(const_name)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
React::NativeLibrary.import_const_from_native(Object, const_name, true) || raise(e)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def method_missing(method, *args, &block)
|
|
21
|
+
component_class = React::NativeLibrary.import_const_from_native(self, method, false)
|
|
22
|
+
_reactrb_original_method_missing(method, *args, &block) unless component_class
|
|
23
|
+
React::RenderingContext.render(component_class, *args, &block)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/logo1.png
ADDED
|
Binary file
|
data/logo2.png
ADDED
|
Binary file
|
data/logo3.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
For example assuming you are releasing fix to 0.8.18
|
|
3
|
+
|
|
4
|
+
1. Checkout 0-8-stable
|
|
5
|
+
2. Update tests, fix the bug and commit the changes.
|
|
6
|
+
3. Build & Release to RubyGems (Remember the version in version.rb should already be 0.8.19)
|
|
7
|
+
4. Create a tag 'v0.8.19' pointing to that commit.
|
|
8
|
+
5. Bump the version in 0-8-stable to 0.8.20 so it will be ready for the next patch level release.
|
|
9
|
+
6. Commit the version bump, and do a `git push --tags` so the new tag goes up
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'rails-controller-testing'
|
|
4
|
+
Rails::Controller::Testing.install
|
|
5
|
+
|
|
6
|
+
class TestController < ActionController::Base; end
|
|
7
|
+
|
|
8
|
+
RSpec.describe TestController, type: :controller do
|
|
9
|
+
render_views
|
|
10
|
+
|
|
11
|
+
describe '#render_component' do
|
|
12
|
+
controller do
|
|
13
|
+
|
|
14
|
+
layout "test_layout"
|
|
15
|
+
|
|
16
|
+
def index
|
|
17
|
+
render_component
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def new
|
|
21
|
+
render_component "Index", {}, layout: :explicit_layout
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'renders with the default layout' do
|
|
26
|
+
get :index
|
|
27
|
+
expect(response).to render_template(layout: :test_layout)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "renders with a specified layout" do
|
|
31
|
+
get :new
|
|
32
|
+
expect(response).to render_template(layout: :explicit_layout)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/spec/index.html.erb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'React::Callbacks', js: true do
|
|
4
|
+
it 'defines callback' do
|
|
5
|
+
on_client do
|
|
6
|
+
class Foo
|
|
7
|
+
include React::Callbacks
|
|
8
|
+
define_callback :before_dinner
|
|
9
|
+
before_dinner :wash_hands
|
|
10
|
+
|
|
11
|
+
def wash_hands;end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
expect_evaluate_ruby do
|
|
16
|
+
instance = Foo.new
|
|
17
|
+
[ instance.respond_to?(:wash_hands), instance.run_callback(:before_dinner) ]
|
|
18
|
+
end.to eq([true, ["wash_hands"]])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'defines multiple callbacks' do
|
|
22
|
+
on_client do
|
|
23
|
+
class Foo
|
|
24
|
+
include React::Callbacks
|
|
25
|
+
define_callback :before_dinner
|
|
26
|
+
before_dinner :wash_hands, :turn_off_laptop
|
|
27
|
+
|
|
28
|
+
def wash_hands;end
|
|
29
|
+
def turn_off_laptop;end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
expect_evaluate_ruby do
|
|
33
|
+
instance = Foo.new
|
|
34
|
+
[ instance.respond_to?(:wash_hands),
|
|
35
|
+
instance.respond_to?(:turn_off_laptop),
|
|
36
|
+
instance.run_callback(:before_dinner) ]
|
|
37
|
+
end.to eq([true, true, ["wash_hands", "turn_off_laptop" ]])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'using Hyperloop::Context.reset!' do
|
|
41
|
+
#after(:all) do
|
|
42
|
+
# Hyperloop::Context.instance_variable_set(:@context, nil)
|
|
43
|
+
#end
|
|
44
|
+
it 'clears callbacks on Hyperloop::Context.reset!' do
|
|
45
|
+
on_client do
|
|
46
|
+
Hyperloop::Context.reset!
|
|
47
|
+
|
|
48
|
+
class Foo
|
|
49
|
+
include React::Callbacks
|
|
50
|
+
define_callback :before_dinner
|
|
51
|
+
|
|
52
|
+
before_dinner :wash_hands, :turn_off_laptop
|
|
53
|
+
|
|
54
|
+
def wash_hands;end
|
|
55
|
+
|
|
56
|
+
def turn_off_laptop;end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
expect_evaluate_ruby do
|
|
60
|
+
instance = Foo.new
|
|
61
|
+
|
|
62
|
+
Hyperloop::Context.reset!
|
|
63
|
+
|
|
64
|
+
Foo.class_eval do
|
|
65
|
+
before_dinner :wash_hands
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
instance.run_callback(:before_dinner)
|
|
69
|
+
end.to eq(["wash_hands"])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'defines block callback' do
|
|
74
|
+
on_client do
|
|
75
|
+
class Foo
|
|
76
|
+
include React::Callbacks
|
|
77
|
+
attr_accessor :a
|
|
78
|
+
attr_accessor :b
|
|
79
|
+
|
|
80
|
+
define_callback :before_dinner
|
|
81
|
+
|
|
82
|
+
before_dinner do
|
|
83
|
+
self.a = 10
|
|
84
|
+
end
|
|
85
|
+
before_dinner do
|
|
86
|
+
self.b = 20
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
expect_evaluate_ruby do
|
|
91
|
+
foo = Foo.new
|
|
92
|
+
foo.run_callback(:before_dinner)
|
|
93
|
+
[ foo.a, foo.b ]
|
|
94
|
+
end.to eq([10, 20])
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'defines multiple callback group' do
|
|
98
|
+
on_client do
|
|
99
|
+
class Foo
|
|
100
|
+
include React::Callbacks
|
|
101
|
+
define_callback :before_dinner
|
|
102
|
+
define_callback :after_dinner
|
|
103
|
+
attr_accessor :a
|
|
104
|
+
|
|
105
|
+
before_dinner do
|
|
106
|
+
self.a = 10
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
expect_evaluate_ruby do
|
|
111
|
+
foo = Foo.new
|
|
112
|
+
foo.run_callback(:before_dinner)
|
|
113
|
+
foo.run_callback(:after_dinner)
|
|
114
|
+
foo.a
|
|
115
|
+
end.to eq(10)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'receives args as callback' do
|
|
119
|
+
on_client do
|
|
120
|
+
class Foo
|
|
121
|
+
include React::Callbacks
|
|
122
|
+
define_callback :before_dinner
|
|
123
|
+
define_callback :after_dinner
|
|
124
|
+
|
|
125
|
+
attr_accessor :lorem
|
|
126
|
+
|
|
127
|
+
before_dinner do |a, b|
|
|
128
|
+
self.lorem = "#{a}-#{b}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
after_dinner :eat_ice_cream
|
|
132
|
+
def eat_ice_cream(a,b,c); end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
expect_evaluate_ruby do
|
|
136
|
+
foo = Foo.new
|
|
137
|
+
foo.run_callback(:before_dinner, 1, 2)
|
|
138
|
+
res1 = foo.run_callback(:after_dinner, 4, 5, 6)
|
|
139
|
+
[res1, foo.lorem]
|
|
140
|
+
end.to eq([["eat_ice_cream"], '1-2'])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'React::Children', js: true do
|
|
4
|
+
describe 'with multiple child elements' do
|
|
5
|
+
before :each do
|
|
6
|
+
on_client do
|
|
7
|
+
class InitTest
|
|
8
|
+
def self.get_children
|
|
9
|
+
component = Class.new do
|
|
10
|
+
include React::Component
|
|
11
|
+
def render
|
|
12
|
+
div { 'lorem' }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
childs = [ React.create_element('a'), React.create_element('li') ]
|
|
16
|
+
element = React.create_element(component) { childs }
|
|
17
|
+
el_children = element.to_n.JS[:props].JS[:children]
|
|
18
|
+
children = React::Children.new(el_children)
|
|
19
|
+
dom_el = JS.call(:eval, "document.body.appendChild(document.createElement('div'))")
|
|
20
|
+
React.render(element, dom_el)
|
|
21
|
+
children
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'is enumerable' do
|
|
28
|
+
expect_evaluate_ruby do
|
|
29
|
+
InitTest.get_children.map { |elem| elem.element_type }
|
|
30
|
+
end.to eq(['a', 'li'])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'returns an Enumerator when not providing a block' do
|
|
34
|
+
expect_evaluate_ruby do
|
|
35
|
+
nodes = InitTest.get_children.each
|
|
36
|
+
[nodes.class.name, nodes.size]
|
|
37
|
+
end.to eq(["Enumerator", 2])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe '#each' do
|
|
41
|
+
it 'returns an array of elements' do
|
|
42
|
+
expect_evaluate_ruby do
|
|
43
|
+
nodes = InitTest.get_children.each { |elem| elem.element_type }
|
|
44
|
+
[nodes.class.name, nodes.map(&:class)]
|
|
45
|
+
end.to eq(["Array", ["React::Element", "React::Element"]])
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe '#length' do
|
|
50
|
+
it 'returns the number of child elements' do
|
|
51
|
+
expect_evaluate_ruby do
|
|
52
|
+
InitTest.get_children.length
|
|
53
|
+
end.to eq(2)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe 'with single child element' do
|
|
59
|
+
before :each do
|
|
60
|
+
on_client do
|
|
61
|
+
class InitTest
|
|
62
|
+
def self.get_children
|
|
63
|
+
component = Class.new do
|
|
64
|
+
include React::Component
|
|
65
|
+
def render
|
|
66
|
+
div { 'lorem' }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
childs = [ React.create_element('a') ]
|
|
70
|
+
element = React.create_element(component) { childs }
|
|
71
|
+
el_children = element.to_n.JS[:props].JS[:children]
|
|
72
|
+
children = React::Children.new(el_children)
|
|
73
|
+
dom_el = JS.call(:eval, "document.body.appendChild(document.createElement('div'))")
|
|
74
|
+
React.render(element, dom_el)
|
|
75
|
+
children
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'is enumerable containing single element' do
|
|
82
|
+
expect_evaluate_ruby do
|
|
83
|
+
InitTest.get_children.map { |elem| elem.element_type }
|
|
84
|
+
end.to eq(["a"])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#length' do
|
|
88
|
+
it 'returns the number of child elements' do
|
|
89
|
+
expect_evaluate_ruby do
|
|
90
|
+
InitTest.get_children.length
|
|
91
|
+
end.to eq(1)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe 'with no child element' do
|
|
97
|
+
before :each do
|
|
98
|
+
on_client do
|
|
99
|
+
class InitTest
|
|
100
|
+
def self.get_children
|
|
101
|
+
component = Class.new do
|
|
102
|
+
include React::Component
|
|
103
|
+
def render
|
|
104
|
+
div { 'lorem' }
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
element = React.create_element(component)
|
|
108
|
+
el_children = element.to_n.JS[:props].JS[:children]
|
|
109
|
+
children = React::Children.new(el_children)
|
|
110
|
+
dom_el = JS.call(:eval, "document.body.appendChild(document.createElement('div'))")
|
|
111
|
+
React.render(element, dom_el)
|
|
112
|
+
children
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'is enumerable containing no elements' do
|
|
119
|
+
expect_evaluate_ruby do
|
|
120
|
+
InitTest.get_children.map { |elem| elem.element_type }
|
|
121
|
+
end.to eq([])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe '#length' do
|
|
125
|
+
it 'returns the number of child elements' do
|
|
126
|
+
expect_evaluate_ruby do
|
|
127
|
+
InitTest.get_children.length
|
|
128
|
+
end.to eq(0)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'React::Component::Base', js: true do
|
|
4
|
+
|
|
5
|
+
before :each do
|
|
6
|
+
on_client do
|
|
7
|
+
class Foo < React::Component::Base
|
|
8
|
+
before_mount do
|
|
9
|
+
@instance_data = ["working"]
|
|
10
|
+
end
|
|
11
|
+
def render
|
|
12
|
+
@instance_data.first
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'can create a simple component class' do
|
|
19
|
+
mount 'Foo'
|
|
20
|
+
expect(page.body[-50..-19]).to match(/<span>working<\/span>/)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'can create a simple component class that can be inherited to create another component class' do
|
|
24
|
+
mount 'Bar' do
|
|
25
|
+
class Bar < Foo
|
|
26
|
+
before_mount do
|
|
27
|
+
@instance_data << "well"
|
|
28
|
+
end
|
|
29
|
+
def render
|
|
30
|
+
@instance_data.join(" ")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
expect(page.body[-50..-19]).to match(/<span>working well<\/span>/)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'React::Component', js: true do
|
|
4
|
+
|
|
5
|
+
it 'defines component spec methods' do
|
|
6
|
+
on_client do
|
|
7
|
+
class Foo
|
|
8
|
+
include React::Component
|
|
9
|
+
def initialize(native = nil)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def render
|
|
13
|
+
React.create_element('div')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
# class methods
|
|
18
|
+
expect_evaluate_ruby("Foo.respond_to?(:initial_state)").to be_truthy
|
|
19
|
+
expect_evaluate_ruby("Foo.respond_to?(:default_props)").to be_truthy
|
|
20
|
+
expect_evaluate_ruby("Foo.respond_to?(:prop_types)").to be_truthy
|
|
21
|
+
# instance_methods
|
|
22
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_will_mount)").to be_truthy
|
|
23
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_did_mount)").to be_truthy
|
|
24
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_will_receive_props)").to be_truthy
|
|
25
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:should_component_update?)").to be_truthy
|
|
26
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_will_update)").to be_truthy
|
|
27
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_did_update)").to be_truthy
|
|
28
|
+
expect_evaluate_ruby("Foo.new.respond_to?(:component_will_unmount)").to be_truthy
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe 'Life Cycle' do
|
|
32
|
+
before(:each) do
|
|
33
|
+
on_client do
|
|
34
|
+
class Foo
|
|
35
|
+
include React::Component
|
|
36
|
+
def self.call_history
|
|
37
|
+
@call_history ||= []
|
|
38
|
+
end
|
|
39
|
+
def render
|
|
40
|
+
React.create_element('div') { 'lorem' }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'invokes `before_mount` registered methods when `componentWillMount()`' do
|
|
47
|
+
mount 'Foo' do
|
|
48
|
+
Foo.class_eval do
|
|
49
|
+
before_mount :bar, :bar2
|
|
50
|
+
def bar; self.class.call_history << "bar"; end
|
|
51
|
+
def bar2; self.class.call_history << "bar2"; end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
expect_evaluate_ruby("Foo.call_history").to eq(["bar", "bar2"])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'invokes `after_mount` registered methods when `componentDidMount()`' do
|
|
58
|
+
mount 'Foo' do
|
|
59
|
+
Foo.class_eval do
|
|
60
|
+
after_mount :bar3, :bar4
|
|
61
|
+
def bar3; self.class.call_history << "bar3"; end
|
|
62
|
+
def bar4; self.class.call_history << "bar4"; end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
expect_evaluate_ruby("Foo.call_history").to eq(["bar3", "bar4"])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'allows multiple class declared life cycle hooker' do
|
|
69
|
+
evaluate_ruby do
|
|
70
|
+
Foo.class_eval do
|
|
71
|
+
before_mount :bar
|
|
72
|
+
def bar; self.class.call_history << "bar"; end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class FooBar
|
|
76
|
+
include React::Component
|
|
77
|
+
after_mount :bar2
|
|
78
|
+
def self.call_history
|
|
79
|
+
@call_history ||= []
|
|
80
|
+
end
|
|
81
|
+
def bar2; self.class.call_history << "bar2"; end
|
|
82
|
+
def render
|
|
83
|
+
React.create_element('div') { 'lorem' }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
87
|
+
instance = React::Test::Utils.render_component_into_document(FooBar)
|
|
88
|
+
end
|
|
89
|
+
expect_evaluate_ruby("Foo.call_history").to eq(["bar"])
|
|
90
|
+
expect_evaluate_ruby("FooBar.call_history").to eq(["bar2"])
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'allows block for life cycle callback' do
|
|
94
|
+
expect_evaluate_ruby do
|
|
95
|
+
Foo.class_eval do
|
|
96
|
+
before_mount do
|
|
97
|
+
set_state({ foo: "bar" })
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
101
|
+
instance.state[:foo]
|
|
102
|
+
end.to eq('bar')
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'invokes :after_error when componentDidCatch' do
|
|
106
|
+
client_option raise_on_js_errors: :off
|
|
107
|
+
mount 'Foo' do
|
|
108
|
+
class ErrorFoo
|
|
109
|
+
include Hyperloop::Component::Mixin
|
|
110
|
+
param :just
|
|
111
|
+
def render
|
|
112
|
+
raise 'ErrorFoo Error'
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
Foo.class_eval do
|
|
116
|
+
def self.get_error
|
|
117
|
+
@@error
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.get_info
|
|
121
|
+
@@info
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def render
|
|
125
|
+
DIV { ErrorFoo(just: :a_param) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
after_error do |error, info|
|
|
129
|
+
@@error = error.message
|
|
130
|
+
@@info = info[:componentStack]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
expect_evaluate_ruby('Foo.get_error').to eq('ErrorFoo Error')
|
|
135
|
+
expect_evaluate_ruby('Foo.get_info').to eq("\n in ErrorFoo\n in div\n in Foo\n in React::TopLevelRailsComponent")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe 'New style setter & getter' do
|
|
140
|
+
before(:each) do
|
|
141
|
+
on_client do
|
|
142
|
+
class Foo
|
|
143
|
+
include React::Component
|
|
144
|
+
def render
|
|
145
|
+
div { state.foo }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'implicitly will create a state variable when first written' do
|
|
152
|
+
mount 'Foo' do
|
|
153
|
+
Foo.class_eval do
|
|
154
|
+
before_mount do
|
|
155
|
+
state.foo! 'bar'
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
# this was a 'have_xpath' check, but these are totally unreliable in capybara with webdrivers
|
|
160
|
+
# leading to false positives and negatives
|
|
161
|
+
# this simple check for string inclusion makes this checks reliable
|
|
162
|
+
expect(page.body[-35..-19]).to include("<div>bar</div>")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'allows kernal method names like "format" to be used as state variable names' do
|
|
166
|
+
mount 'Foo' do
|
|
167
|
+
Foo.class_eval do
|
|
168
|
+
before_mount do
|
|
169
|
+
state.format! 'yes'
|
|
170
|
+
state.foo! state.format
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
expect(page.body[-35..-19]).to include("<div>yes</div>")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'returns an observer with the bang method and no arguments' do
|
|
178
|
+
mount 'Foo' do
|
|
179
|
+
Foo.class_eval do
|
|
180
|
+
before_mount do
|
|
181
|
+
state.foo!(state.baz!.class.name)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
expect(page.body[-50..-19]).to include("<div>React::Observable</div>")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it 'returns the current value of a state when written' do
|
|
189
|
+
mount 'Foo' do
|
|
190
|
+
Foo.class_eval do
|
|
191
|
+
before_mount do
|
|
192
|
+
state.baz! 'bar'
|
|
193
|
+
state.foo!(state.baz!('pow'))
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
expect(page.body[-35..-19]).to include("<div>bar</div>")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'can access an explicitly defined state`' do
|
|
201
|
+
mount 'Foo' do
|
|
202
|
+
Foo.class_eval do
|
|
203
|
+
define_state foo: :bar
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
expect(page.body[-35..-19]).to include("<div>bar</div>")
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
describe 'State setter & getter' do
|
|
211
|
+
before(:each) do
|
|
212
|
+
on_client do
|
|
213
|
+
class Foo
|
|
214
|
+
include React::Component
|
|
215
|
+
def render
|
|
216
|
+
React.create_element('div') { 'lorem' }
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'defines setter using `define_state`' do
|
|
223
|
+
expect_evaluate_ruby do
|
|
224
|
+
Foo.class_eval do
|
|
225
|
+
define_state :foo
|
|
226
|
+
before_mount :set_up
|
|
227
|
+
def set_up
|
|
228
|
+
mutate.foo 'bar'
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
232
|
+
instance.state.foo
|
|
233
|
+
end.to eq('bar')
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it 'defines init state by passing a block to `define_state`' do
|
|
237
|
+
expect_evaluate_ruby do
|
|
238
|
+
element_to_render = React.create_element(Foo)
|
|
239
|
+
Foo.class_eval do
|
|
240
|
+
define_state(:foo) { 10 }
|
|
241
|
+
end
|
|
242
|
+
dom_el = JS.call(:eval, "document.body.appendChild(document.createElement('div'))")
|
|
243
|
+
instance = React.render(element_to_render, dom_el)
|
|
244
|
+
instance.state.foo
|
|
245
|
+
end.to eq(10)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'defines getter using `define_state`' do
|
|
249
|
+
expect_evaluate_ruby do
|
|
250
|
+
Foo.class_eval do
|
|
251
|
+
define_state(:foo) { 10 }
|
|
252
|
+
before_mount :bump
|
|
253
|
+
def bump
|
|
254
|
+
mutate.foo(state.foo + 20)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
258
|
+
instance.state.foo
|
|
259
|
+
end.to eq(30)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it 'defines multiple state accessors by passing array to `define_state`' do
|
|
263
|
+
expect_evaluate_ruby do
|
|
264
|
+
Foo.class_eval do
|
|
265
|
+
define_state :foo, :foo2
|
|
266
|
+
before_mount :set_up
|
|
267
|
+
def set_up
|
|
268
|
+
mutate.foo 10
|
|
269
|
+
mutate.foo2 20
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
273
|
+
[ instance.state.foo, instance.state.foo2 ]
|
|
274
|
+
end.to eq([10, 20])
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it 'invokes `define_state` multiple times to define states' do
|
|
278
|
+
expect_evaluate_ruby do
|
|
279
|
+
Foo.class_eval do
|
|
280
|
+
define_state(:foo) { 30 }
|
|
281
|
+
define_state(:foo2) { 40 }
|
|
282
|
+
end
|
|
283
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
284
|
+
[ instance.state.foo, instance.state.foo2 ]
|
|
285
|
+
end.to eq([30, 40])
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it 'can initialize multiple state variables with a block' do
|
|
289
|
+
expect_evaluate_ruby do
|
|
290
|
+
Foo.class_eval do
|
|
291
|
+
define_state(:foo, :foo2) { 30 }
|
|
292
|
+
end
|
|
293
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
294
|
+
[ instance.state.foo, instance.state.foo2 ]
|
|
295
|
+
end.to eq([30, 30])
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
it 'can mix multiple state variables with initializers and a block' do
|
|
299
|
+
expect_evaluate_ruby do
|
|
300
|
+
Foo.class_eval do
|
|
301
|
+
define_state(:x, :y, foo: 1, bar: 2) {3}
|
|
302
|
+
end
|
|
303
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
304
|
+
[ instance.state.x, instance.state.y, instance.state.foo, instance.state.bar ]
|
|
305
|
+
end.to eq([3, 3, 1, 2])
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it 'gets state in render method' do
|
|
309
|
+
mount 'Foo' do
|
|
310
|
+
Foo.class_eval do
|
|
311
|
+
define_state(:foo) { 10 }
|
|
312
|
+
def render
|
|
313
|
+
React.create_element('div') { state.foo }
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
expect(page.body[-35..-19]).to include("<div>10</div>")
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'supports original `setState` as `set_state` method' do
|
|
321
|
+
expect_evaluate_ruby do
|
|
322
|
+
Foo.class_eval do
|
|
323
|
+
before_mount do
|
|
324
|
+
self.set_state(foo: 'bar')
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
328
|
+
instance.state[:foo]
|
|
329
|
+
end.to eq('bar')
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it '`set_state!` method works and doesnt replace other state' do
|
|
333
|
+
# this test changed because the function replaceState is gone in react
|
|
334
|
+
expect_evaluate_ruby do
|
|
335
|
+
Foo.class_eval do
|
|
336
|
+
before_mount do
|
|
337
|
+
set_state(foo: 'bar')
|
|
338
|
+
set_state!(bar: 'lorem')
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
342
|
+
[ instance.state[:foo], instance.state[:bar] ]
|
|
343
|
+
end.to eq(['bar', 'lorem'])
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it 'supports original `state` method' do
|
|
347
|
+
mount 'Foo' do
|
|
348
|
+
Foo.class_eval do
|
|
349
|
+
before_mount do
|
|
350
|
+
self.set_state(foo: 'bar')
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def render
|
|
354
|
+
div { self.state[:foo] }
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
expect(page.body[-35..-19]).to include("<div>bar</div>")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it 'transforms state getter to Ruby object' do
|
|
362
|
+
mount 'Foo' do
|
|
363
|
+
Foo.class_eval do
|
|
364
|
+
define_state :foo
|
|
365
|
+
|
|
366
|
+
before_mount do
|
|
367
|
+
mutate.foo [{a: "Hello"}]
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def render
|
|
371
|
+
div { state.foo[0][:a] }
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
expect(page.body[-40..-19]).to include("<div>Hello</div>")
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it 'sets initial state with default value in constructor in @native object state property' do
|
|
379
|
+
mount 'StateFoo' do
|
|
380
|
+
class StateFoo
|
|
381
|
+
include Hyperloop::Component::Mixin
|
|
382
|
+
state bar: 25
|
|
383
|
+
|
|
384
|
+
def initialize(native)
|
|
385
|
+
super(native)
|
|
386
|
+
@@initial_state = @native.JS[:state].JS[:bar]
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def self.initial_state
|
|
390
|
+
@@initial_state ||= 0
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def render
|
|
394
|
+
React.create_element('div') { 'lorem' }
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
expect_evaluate_ruby('StateFoo.initial_state').to eq(25)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it 'doesnt cause extra render when setting initial state' do
|
|
402
|
+
mount 'StateFoo' do
|
|
403
|
+
class StateFoo
|
|
404
|
+
include Hyperloop::Component::Mixin
|
|
405
|
+
state bar: 25
|
|
406
|
+
|
|
407
|
+
def self.render_count
|
|
408
|
+
@@render_count ||= 0
|
|
409
|
+
end
|
|
410
|
+
def self.incr_render_count
|
|
411
|
+
@@render_count ||= 0
|
|
412
|
+
@@render_count += 1
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def render
|
|
416
|
+
StateFoo.incr_render_count
|
|
417
|
+
React.create_element('div') { 'lorem' }
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
expect_evaluate_ruby('StateFoo.render_count').to eq(1)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it 'doesnt cause extra render when setting state in :before_mount' do
|
|
425
|
+
mount 'StateFoo' do
|
|
426
|
+
class StateFoo
|
|
427
|
+
include Hyperloop::Component::Mixin
|
|
428
|
+
|
|
429
|
+
def self.render_count
|
|
430
|
+
@@render_count ||= 0
|
|
431
|
+
end
|
|
432
|
+
def self.incr_render_count
|
|
433
|
+
@@render_count ||= 0
|
|
434
|
+
@@render_count += 1
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
before_mount do
|
|
438
|
+
mutate.bar 50
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def render
|
|
442
|
+
StateFoo.incr_render_count
|
|
443
|
+
React.create_element('div') { 'lorem' }
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
expect_evaluate_ruby('StateFoo.render_count').to eq(1)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
it 'doesnt cause extra render when setting state in :before_receive_props' do
|
|
452
|
+
mount 'Foo' do
|
|
453
|
+
class StateFoo
|
|
454
|
+
include Hyperloop::Component::Mixin
|
|
455
|
+
|
|
456
|
+
param :drinks
|
|
457
|
+
|
|
458
|
+
def self.render_count
|
|
459
|
+
@@render_count ||= 0
|
|
460
|
+
end
|
|
461
|
+
def self.incr_render_count
|
|
462
|
+
@@render_count ||= 0
|
|
463
|
+
@@render_count += 1
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
before_receive_props do |new_params|
|
|
467
|
+
mutate.bar 50
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def render
|
|
471
|
+
StateFoo.incr_render_count
|
|
472
|
+
React.create_element('div') { 'lorem' }
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
Foo.class_eval do
|
|
477
|
+
define_state :foo
|
|
478
|
+
|
|
479
|
+
before_mount do
|
|
480
|
+
state.foo 25
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def render
|
|
484
|
+
div { StateFoo(drinks: state.foo) }
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
after_mount do
|
|
488
|
+
mutate.foo 50
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
expect_evaluate_ruby('StateFoo.render_count').to eq(2)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
describe 'Props' do
|
|
497
|
+
describe 'this.props could be accessed through `params` method' do
|
|
498
|
+
before do
|
|
499
|
+
on_client do
|
|
500
|
+
class Foo
|
|
501
|
+
include React::Component
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
it 'reads from parent passed properties through `params`' do
|
|
507
|
+
mount 'Foo', prop: 'foobar' do
|
|
508
|
+
Foo.class_eval do
|
|
509
|
+
param :prop
|
|
510
|
+
def render
|
|
511
|
+
React.create_element('div') { params[:prop] }
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
expect(page.body[-40..-19]).to include("<div>foobar</div>")
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
it 'accesses nested params as orignal Ruby object' do
|
|
519
|
+
mount 'Foo', prop: [{foo: 10}] do
|
|
520
|
+
Foo.class_eval do
|
|
521
|
+
param :prop
|
|
522
|
+
def render
|
|
523
|
+
React.create_element('div') { params[:prop][0][:foo] }
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
expect(page.body[-35..-19]).to include("<div>10</div>")
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
describe 'Props Updating', v13_only: true do
|
|
532
|
+
before do
|
|
533
|
+
on_client do
|
|
534
|
+
class Foo
|
|
535
|
+
include React::Component
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
it '`setProps` as method `set_props` is no longer supported' do
|
|
541
|
+
expect_evaluate_ruby do
|
|
542
|
+
Foo.class_eval do
|
|
543
|
+
param :foo
|
|
544
|
+
def render
|
|
545
|
+
React.create_element('div') { params[:foo] }
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
instance = React::Test::Utils.render_component_into_document(Foo, foo: 10)
|
|
549
|
+
begin
|
|
550
|
+
instance.set_props(foo: 20)
|
|
551
|
+
rescue
|
|
552
|
+
'got risen'
|
|
553
|
+
end
|
|
554
|
+
end.to eq('got risen')
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it 'original `replaceProps` as method `set_props!` is no longer supported' do
|
|
558
|
+
expect_evaluate_ruby do
|
|
559
|
+
Foo.class_eval do
|
|
560
|
+
param :foo
|
|
561
|
+
def render
|
|
562
|
+
React.create_element('div') { params[:foo] ? 'exist' : 'null' }
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
instance = React::Test::Utils.render_component_into_document(Foo, foo: 10)
|
|
566
|
+
begin
|
|
567
|
+
instance.set_props!(bar: 20)
|
|
568
|
+
rescue
|
|
569
|
+
'got risen'
|
|
570
|
+
end
|
|
571
|
+
end.to eq('got risen')
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
describe 'Prop validation' do
|
|
576
|
+
before do
|
|
577
|
+
on_client do
|
|
578
|
+
class Foo
|
|
579
|
+
include Hyperloop::Component::Mixin
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
it 'specifies validation rules using `params` class method' do
|
|
585
|
+
expect_evaluate_ruby do
|
|
586
|
+
Foo.class_eval do
|
|
587
|
+
params do
|
|
588
|
+
requires :foo, type: String
|
|
589
|
+
optional :bar
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
Foo.prop_types
|
|
593
|
+
end.to have_key('_componentValidator')
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it 'logs error in warning if validation failed' do
|
|
597
|
+
evaluate_ruby do
|
|
598
|
+
class Lorem; end
|
|
599
|
+
Foo.class_eval do
|
|
600
|
+
params do
|
|
601
|
+
requires :foo
|
|
602
|
+
requires :lorem, type: Lorem
|
|
603
|
+
optional :bar, type: String
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def render; div; end
|
|
607
|
+
end
|
|
608
|
+
React::Test::Utils.render_component_into_document(Foo, bar: 10, lorem: Lorem.new)
|
|
609
|
+
end
|
|
610
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
611
|
+
.to match(/Warning: Failed prop( type|Type): In component `Foo`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String/)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
it 'should not log anything if validation pass' do
|
|
615
|
+
evaluate_ruby do
|
|
616
|
+
class Lorem; end
|
|
617
|
+
Foo.class_eval do
|
|
618
|
+
params do
|
|
619
|
+
requires :foo
|
|
620
|
+
requires :lorem, type: Lorem
|
|
621
|
+
optional :bar, type: String
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def render; div; end
|
|
625
|
+
end
|
|
626
|
+
React::Test::Utils.render_component_into_document(Foo, foo: 10, bar: '10', lorem: Lorem.new)
|
|
627
|
+
end
|
|
628
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n")).to_not match(/prop/)
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
describe 'Default props' do
|
|
633
|
+
it 'sets default props using validation helper' do
|
|
634
|
+
on_client do
|
|
635
|
+
class Foo
|
|
636
|
+
include React::Component
|
|
637
|
+
params do
|
|
638
|
+
optional :foo, default: 'foo'
|
|
639
|
+
optional :bar, default: 'bar'
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def render
|
|
643
|
+
div { params[:foo] + '-' + params[:bar]}
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
mount 'Foo'
|
|
648
|
+
expect(page.body[-40..-19]).to include("<div>foo-bar</div>")
|
|
649
|
+
mount 'Foo', foo: 'lorem'
|
|
650
|
+
expect(page.body[-40..-19]).to include("<div>lorem-bar</div>")
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
describe 'Anonymous Component' do
|
|
656
|
+
it "will not generate spurious warning messages" do
|
|
657
|
+
evaluate_ruby do
|
|
658
|
+
foo = Class.new(React::Component::Base)
|
|
659
|
+
foo.class_eval do
|
|
660
|
+
def render; "hello" end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
React::Test::Utils.render_component_into_document(foo)
|
|
664
|
+
end
|
|
665
|
+
expect(page.driver.browser.manage.logs.get(:browser)
|
|
666
|
+
.reject { |entry| entry.to_s.include?("Deprecated feature") }
|
|
667
|
+
.map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n").size)
|
|
668
|
+
.to eq(0)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
describe 'Render Error Handling' do
|
|
673
|
+
it "will generate a message if render returns something other than an Element or a String" do
|
|
674
|
+
mount 'Foo' do
|
|
675
|
+
class Foo < React::Component::Base
|
|
676
|
+
def render; Hash.new; end
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
680
|
+
.to match(/Instead the Hash \{\} was returned/)
|
|
681
|
+
end
|
|
682
|
+
it "will generate a message if render returns a Component class" do
|
|
683
|
+
mount 'Foo' do
|
|
684
|
+
class Foo < React::Component::Base
|
|
685
|
+
def render; Foo; end
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
689
|
+
.to match(/Did you mean Foo()/)
|
|
690
|
+
end
|
|
691
|
+
it "will generate a message if more than 1 element is generated" do
|
|
692
|
+
mount 'Foo' do
|
|
693
|
+
class Foo < React::Component::Base
|
|
694
|
+
def render; "hello".span; "goodby".span; end
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
698
|
+
.to match(/Instead 2 elements were generated/)
|
|
699
|
+
end
|
|
700
|
+
it "will generate a message if the element generated is not the element returned" do
|
|
701
|
+
mount 'Foo' do
|
|
702
|
+
class Foo < React::Component::Base
|
|
703
|
+
def render; "hello".span; "goodby".span.delete; end
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
707
|
+
.to match(/A different element was returned than was generated within the DSL/)
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
describe 'Event handling' do
|
|
712
|
+
before do
|
|
713
|
+
on_client do
|
|
714
|
+
class Foo
|
|
715
|
+
include React::Component
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it 'works in render method' do
|
|
721
|
+
expect_evaluate_ruby do
|
|
722
|
+
Foo.class_eval do
|
|
723
|
+
define_state(:clicked) { false }
|
|
724
|
+
|
|
725
|
+
def render
|
|
726
|
+
React.create_element('div').on(:click) do
|
|
727
|
+
mutate.clicked true
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
instance = React::Test::Utils.render_component_into_document(Foo)
|
|
732
|
+
React::Test::Utils.simulate_click(instance)
|
|
733
|
+
instance.state.clicked
|
|
734
|
+
end.to eq(true)
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
it 'invokes handler on `this.props` using emit' do
|
|
738
|
+
on_client do
|
|
739
|
+
Foo.class_eval do
|
|
740
|
+
param :on_foo_fubmit, type: Proc
|
|
741
|
+
after_mount :setup
|
|
742
|
+
|
|
743
|
+
def setup
|
|
744
|
+
self.emit(:foo_submit, 'bar')
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def render
|
|
748
|
+
React.create_element('div')
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
evaluate_ruby do
|
|
753
|
+
element = React.create_element(Foo).on(:foo_submit) { 'bar' }
|
|
754
|
+
React::Test::Utils.render_into_document(element)
|
|
755
|
+
end
|
|
756
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
757
|
+
.to_not match(/Exception raised/)
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
it 'invokes handler with multiple params using emit' do
|
|
761
|
+
on_client do
|
|
762
|
+
Foo.class_eval do
|
|
763
|
+
param :on_foo_invoked, type: Proc
|
|
764
|
+
after_mount :setup
|
|
765
|
+
|
|
766
|
+
def setup
|
|
767
|
+
self.emit(:foo_invoked, [1,2,3], 'bar')
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def render
|
|
771
|
+
React.create_element('div')
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
evaluate_ruby do
|
|
777
|
+
element = React.create_element(Foo).on(:foo_invoked) { return [1,2,3], 'bar' }
|
|
778
|
+
React::Test::Utils.render_into_document(element)
|
|
779
|
+
end
|
|
780
|
+
expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
|
|
781
|
+
.to_not match(/Exception raised/)
|
|
782
|
+
end
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
describe '#render' do
|
|
786
|
+
it 'supports element building helpers' do
|
|
787
|
+
on_client do
|
|
788
|
+
class Foo
|
|
789
|
+
include React::Component
|
|
790
|
+
param :foo
|
|
791
|
+
def render
|
|
792
|
+
div do
|
|
793
|
+
span { params[:foo] }
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
class Bar
|
|
799
|
+
include React::Component
|
|
800
|
+
def render
|
|
801
|
+
div do
|
|
802
|
+
React::RenderingContext.render(Foo, foo: 'astring')
|
|
803
|
+
end
|
|
804
|
+
end
|
|
805
|
+
end
|
|
806
|
+
end
|
|
807
|
+
evaluate_ruby do
|
|
808
|
+
React::Test::Utils.render_component_into_document(Bar)
|
|
809
|
+
end
|
|
810
|
+
expect(page.body[-80..-19]).to include("<div><div><span>astring</span></div></div>")
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
it 'builds single node in top-level render without providing a block' do
|
|
814
|
+
mount 'Foo' do
|
|
815
|
+
class Foo
|
|
816
|
+
include React::Component
|
|
817
|
+
|
|
818
|
+
def render
|
|
819
|
+
div
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
expect(page.body).to include('<div data-react-class="React.TopLevelRailsComponent" data-react-props="{"render_params":{},"component_name":"Foo","controller":"ReactTest"}"><div></div></div>')
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
it 'redefines `p` to make method missing work' do
|
|
827
|
+
mount 'Foo' do
|
|
828
|
+
class Foo
|
|
829
|
+
include React::Component
|
|
830
|
+
|
|
831
|
+
def render
|
|
832
|
+
div {
|
|
833
|
+
p(class_name: 'foo')
|
|
834
|
+
p
|
|
835
|
+
div { 'lorem ipsum' }
|
|
836
|
+
p(id: '10')
|
|
837
|
+
}
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
end
|
|
841
|
+
expect(page.body).to include('<div><p class="foo"></p><p></p><div>lorem ipsum</div><p id="10"></p></div>')
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
it 'only overrides `p` in render context' do
|
|
845
|
+
mount 'Foo' do
|
|
846
|
+
|
|
847
|
+
class Foo
|
|
848
|
+
include React::Component
|
|
849
|
+
|
|
850
|
+
def self.result
|
|
851
|
+
@@result ||= 'ooopsy'
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def self.result_two
|
|
855
|
+
@@result_two ||= 'ooopsy'
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
before_mount do
|
|
859
|
+
@@result = p 'first'
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
after_mount do
|
|
863
|
+
@@result_two = p 'second'
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def render
|
|
867
|
+
p do
|
|
868
|
+
'third'
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
expect_evaluate_ruby('Kernel.p "first"').to eq('first')
|
|
874
|
+
expect_evaluate_ruby('p "second"').to eq('second')
|
|
875
|
+
expect_evaluate_ruby('Foo.result').to eq('first')
|
|
876
|
+
expect_evaluate_ruby('Foo.result_two').to eq('second')
|
|
877
|
+
expect(page.body[-40..-10]).to include("<p>third</p>")
|
|
878
|
+
expect(page.body[-40..-10]).not_to include("<p>first</p>")
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
describe 'new react 15/16 custom isMounted implementation' do
|
|
883
|
+
it 'returns true if after mounted' do
|
|
884
|
+
expect_evaluate_ruby do
|
|
885
|
+
class Foo
|
|
886
|
+
include React::Component
|
|
887
|
+
|
|
888
|
+
def render
|
|
889
|
+
React.create_element('div')
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
component = React::Test::Utils.render_component_into_document(Foo)
|
|
894
|
+
component.mounted?
|
|
895
|
+
end.to eq(true)
|
|
896
|
+
end
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
describe '.params_changed?' do
|
|
900
|
+
|
|
901
|
+
before(:each) do
|
|
902
|
+
on_client do
|
|
903
|
+
class Foo < React::Component::Base
|
|
904
|
+
def needs_update?(next_params, next_state)
|
|
905
|
+
next_params.changed?
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
end
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
it "returns false if new and old params are the same" do
|
|
912
|
+
expect_evaluate_ruby do
|
|
913
|
+
@foo = Foo.new(nil)
|
|
914
|
+
@foo.instance_eval { @native.JS[:props] = JS.call(:eval, 'function bla(){return {value1: 1, value2: 2};}bla();') }
|
|
915
|
+
@foo.should_component_update?({ value2: 2, value1: 1 }, {})
|
|
916
|
+
end.to be_falsy
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
it "returns true if new and old params are have different values" do
|
|
920
|
+
expect_evaluate_ruby do
|
|
921
|
+
@foo = Foo.new(nil)
|
|
922
|
+
@foo.instance_eval { @native.JS[:props] = JS.call(:eval, 'function bla(){return {value1: 1, value2: 2};}bla();') }
|
|
923
|
+
@foo.should_component_update?({value2: 2, value1: 2}, {})
|
|
924
|
+
end.to be_truthy
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
it "returns true if new and old params are have different keys" do
|
|
928
|
+
expect_evaluate_ruby do
|
|
929
|
+
@foo = Foo.new(nil)
|
|
930
|
+
@foo.instance_eval { @native.JS[:props] = JS.call(:eval, 'function bla(){return {value1: 1, value2: 2};}bla();') }
|
|
931
|
+
@foo.should_component_update?({value2: 2, value1: 1, value3: 3}, {})
|
|
932
|
+
end.to be_truthy
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
describe '#should_component_update?' do
|
|
937
|
+
|
|
938
|
+
before(:each) do
|
|
939
|
+
on_client do
|
|
940
|
+
class Foo < React::Component::Base
|
|
941
|
+
def needs_update?(next_params, next_state)
|
|
942
|
+
next_state.changed?
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
EMPTIES = [`{}`, `undefined`, `null`, `false`]
|
|
947
|
+
end
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
it "returns false if both new and old states are empty" do
|
|
951
|
+
expect_evaluate_ruby do
|
|
952
|
+
@foo = Foo.new(nil)
|
|
953
|
+
return_values = []
|
|
954
|
+
EMPTIES.each do |empty1|
|
|
955
|
+
EMPTIES.each do |empty2|
|
|
956
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return #{empty1};}bla();") }
|
|
957
|
+
return_values << @foo.should_component_update?({}, Hash.new(empty2))
|
|
958
|
+
end
|
|
959
|
+
end
|
|
960
|
+
return_values
|
|
961
|
+
end.to all( be_falsy )
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
it "returns true if old state is empty, but new state is not" do
|
|
965
|
+
expect_evaluate_ruby do
|
|
966
|
+
@foo = Foo.new(nil)
|
|
967
|
+
return_values = []
|
|
968
|
+
EMPTIES.each do |empty|
|
|
969
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return #{empty};}bla();") }
|
|
970
|
+
return_values << @foo.should_component_update?({}, {foo: 12})
|
|
971
|
+
end
|
|
972
|
+
return_values
|
|
973
|
+
end.to all( be_truthy )
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
it "returns true if new state is empty, but old state is not" do
|
|
977
|
+
expect_evaluate_ruby do
|
|
978
|
+
@foo = Foo.new(nil)
|
|
979
|
+
return_values = []
|
|
980
|
+
EMPTIES.each do |empty|
|
|
981
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return {foo: 12};}bla();") }
|
|
982
|
+
return_values << @foo.should_component_update?({}, Hash.new(empty))
|
|
983
|
+
end
|
|
984
|
+
return_values
|
|
985
|
+
end.to all( be_truthy )
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
it "returns true if new state and old state have different time stamps" do
|
|
989
|
+
expect_evaluate_ruby do
|
|
990
|
+
@foo = Foo.new(nil)
|
|
991
|
+
return_values = []
|
|
992
|
+
EMPTIES.each do |empty|
|
|
993
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return {'***_state_updated_at-***': 12};}bla();") }
|
|
994
|
+
return_values << @foo.should_component_update?({}, {'***_state_updated_at-***' => 13})
|
|
995
|
+
end
|
|
996
|
+
return_values
|
|
997
|
+
end.to all ( be_truthy )
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
it "returns false if new state and old state have the same time stamps" do
|
|
1001
|
+
expect_evaluate_ruby do
|
|
1002
|
+
@foo = Foo.new(nil)
|
|
1003
|
+
return_values = []
|
|
1004
|
+
EMPTIES.each do |empty|
|
|
1005
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return {'***_state_updated_at-***': 12};}bla();") }
|
|
1006
|
+
return_values << @foo.should_component_update?({}, {'***_state_updated_at-***' => 12})
|
|
1007
|
+
end
|
|
1008
|
+
return_values
|
|
1009
|
+
end.to all( be_falsy )
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
it "returns true if new state without timestamp is different from old state" do
|
|
1013
|
+
expect_evaluate_ruby do
|
|
1014
|
+
@foo = Foo.new(nil)
|
|
1015
|
+
return_values = []
|
|
1016
|
+
EMPTIES.each do |empty|
|
|
1017
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return {'my_state': 12};}bla();") }
|
|
1018
|
+
return_values << @foo.should_component_update?({}, {'my-state' => 13})
|
|
1019
|
+
end
|
|
1020
|
+
return_values
|
|
1021
|
+
end.to all ( be_truthy )
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
it "returns false if new state without timestamp is the same as old state" do
|
|
1025
|
+
expect_evaluate_ruby do
|
|
1026
|
+
@foo = Foo.new(nil)
|
|
1027
|
+
return_values = []
|
|
1028
|
+
EMPTIES.each do |empty|
|
|
1029
|
+
@foo.instance_eval { @native.JS[:state] = JS.call(:eval, "function bla(){return {'my_state': 12};}bla();") }
|
|
1030
|
+
return_values << @foo.should_component_update?({}, {'my_state' => 12})
|
|
1031
|
+
end
|
|
1032
|
+
return_values
|
|
1033
|
+
end.to all( be_falsy )
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
describe '#children' do
|
|
1038
|
+
before(:each) do
|
|
1039
|
+
on_client do
|
|
1040
|
+
class Foo
|
|
1041
|
+
include React::Component
|
|
1042
|
+
def render
|
|
1043
|
+
React.create_element('div') { 'lorem' }
|
|
1044
|
+
end
|
|
1045
|
+
end
|
|
1046
|
+
end
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
it 'returns React::Children collection with child elements' do
|
|
1050
|
+
evaluate_ruby do
|
|
1051
|
+
ele = React.create_element(Foo) {
|
|
1052
|
+
[React.create_element('a'), React.create_element('li')]
|
|
1053
|
+
}
|
|
1054
|
+
instance = React::Test::Utils.render_into_document(ele)
|
|
1055
|
+
|
|
1056
|
+
CHILDREN = instance.children
|
|
1057
|
+
end
|
|
1058
|
+
expect_evaluate_ruby("CHILDREN.class.name").to eq('React::Children')
|
|
1059
|
+
expect_evaluate_ruby("CHILDREN.count").to eq(2)
|
|
1060
|
+
expect_evaluate_ruby("CHILDREN.map(&:element_type)").to eq(['a', 'li'])
|
|
1061
|
+
end
|
|
1062
|
+
|
|
1063
|
+
it 'returns an empty Enumerator if there are no children' do
|
|
1064
|
+
evaluate_ruby do
|
|
1065
|
+
ele = React.create_element(Foo)
|
|
1066
|
+
instance = React::Test::Utils.render_into_document(ele)
|
|
1067
|
+
NODES = instance.children.each
|
|
1068
|
+
end
|
|
1069
|
+
expect_evaluate_ruby("NODES.size").to eq(0)
|
|
1070
|
+
expect_evaluate_ruby("NODES.count").to eq(0)
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
end
|