require 'spec_helper' if opal? describe 'the param macro', type: :component do it 'defines collect_other_params_as method on params proxy' do stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do collect_other_params_as :foo def render div { params.foo[:bar] } end end expect(Foo).to render('
biz
').with_params(bar: 'biz') end it "can create and access a required param" do stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do param :foo def render div { params.foo } end end expect(Foo).to render('
bar
').with_params(foo: :bar) end it "can create and access an optional params" do stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do param foo1: :no_bar1 param foo2: :no_bar2 param :foo3, default: :no_bar3 param :foo4, default: :no_bar4 def render div { "#{params.foo1}-#{params.foo2}-#{params.foo3}-#{params.foo4}" } end end expect(Foo).to render('
bar1-no_bar2-bar3-no_bar4
').with_params(foo1: :bar1, foo3: :bar3) end it 'can specify validation rules with the type option' do stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do param :foo, type: String end expect(Foo.prop_types).to have_key(:_componentValidator) end it "can type check params" do stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do param :foo1, type: String param :foo2, type: String def render div { "#{params.foo1}-#{params.foo2}" } end end expect(Foo).to render('
12-string
').with_params(foo1: 12, foo2: "string") end it 'logs error in warning if validation failed' do stub_const 'Lorem', Class.new stub_const 'Foo2', Class.new(React::Component::Base) Foo2.class_eval do param :foo param :lorem, type: Lorem param :bar, default: nil, type: String def render; div; end end %x{ var log = []; var org_warn_console = window.console.warn; var org_error_console = window.console.error; window.console.warn = window.console.error = function(str){log.push(str)} } renderToDocument(Foo2, bar: 10, lorem: Lorem.new) `window.console.warn = org_warn_console; window.console.error = org_error_console;` 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/) end it 'should not log anything if validation passes' do stub_const 'Lorem', Class.new stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval do param :foo param :lorem, type: Lorem param :bar, default: nil, type: String def render; div; end end %x{ var log = []; var org_warn_console = window.console.warn; var org_error_console = window.console.error; window.console.warn = window.console.error = function(str){log.push(str)} } renderToDocument(Foo, foo: 10, bar: '10', lorem: Lorem.new) `window.console.warn = org_warn_console; window.console.error = org_error_console;` expect(`log`).to eq([]) end describe 'advanced type handling' do before(:each) do %x{ window.dummy_log = []; window.org_warn_console = window.console.warn; window.org_error_console = window.console.warn window.console.warn = window.console.error = function(str){window.dummy_log.push(str)} } stub_const 'Foo', Class.new(React::Component::Base) Foo.class_eval { def render; ""; end } end after(:each) do `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console` end it "can use the [] notation for arrays" do Foo.class_eval do param :foo, type: [] param :bar, type: [] end renderToDocument(Foo, foo: 10, bar: [10]) expect(`window.dummy_log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to Array/) end it "can use the [xxx] notation for arrays of a specific type" do Foo.class_eval do param :foo, type: [String] param :bar, type: [String] end renderToDocument(Foo, foo: [10], bar: ["10"]) 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/) end it "can convert a json hash to a type" do stub_const "BazWoggle", Class.new BazWoggle.class_eval do def initialize(kind) @kind = kind end attr_accessor :kind def self._react_param_conversion(json, validate_only) new(json[:bazwoggle]) if json[:bazwoggle] end end Foo.class_eval do param :foo, type: BazWoggle param :bar, type: BazWoggle param :baz, type: [BazWoggle] def render "#{params.bar.kind}, #{params.baz[0].kind}" end end params = { foo: "", bar: { bazwoggle: 1 }, baz: [{ bazwoggle: 2 }] } expect(Foo).to render('1, 2').with_params(params) expect(`window.dummy_log[0]`).to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle/) end describe "converts params only once" do it "not on every access" do stub_const "BazWoggle", Class.new BazWoggle.class_eval do def initialize(kind) @kind = kind end attr_accessor :kind def self._react_param_conversion(json, validate_only) new(json[:bazwoggle]) if json[:bazwoggle] end end Foo.class_eval do param :foo, type: BazWoggle def render params.foo.kind = params.foo.kind+1 "#{params.foo.kind}" end end expect(React.render_to_static_markup(React.create_element(Foo, foo: {bazwoggle: 1}))).to eq('2') end it "even if contains an embedded native object" do pending 'Fix after merging' stub_const "Bar", Class.new(React::Component::Base) stub_const "BazWoggle", Class.new BazWoggle.class_eval do def initialize(kind) @kind = kind end attr_accessor :kind def self._react_param_conversion(json, validate_only) new(JSON.from_object(json[0])[:bazwoggle]) if JSON.from_object(json[0])[:bazwoggle] end end Bar.class_eval do param :foo, type: BazWoggle def render params.foo.kind.to_s end end Foo.class_eval do export_state :change_me before_mount do Foo.change_me! "initial" end def render Bar(foo: Native([`{bazwoggle: #{Foo.change_me}}`])) end end div = `document.createElement("div")` React.render(React.create_element(Foo, {}), div) Foo.change_me! "updated" expect(`div.children[0].innerHTML`).to eq("updated") end end it "will alias a Proc type param" do Foo.class_eval do param :foo, type: Proc def render params.foo end end expect(Foo).to render('works!').with_params(foo: lambda { 'works!' }) end it "will create a 'bang' (i.e. update) method if the type is React::Observable" do Foo.class_eval do param :foo, type: React::Observable before_mount do params.foo! "ha!" end def render params.foo end end current_state = "" observer = React::Observable.new(current_state) { |new_state| current_state = new_state } expect(Foo).to render('ha!').with_params(foo: observer) expect(current_state).to eq("ha!") end end end end