From 3a3b40ecfd0c0da74e649f9afac85020a8ab763a Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 9 May 2016 13:59:34 +0200 Subject: [PATCH] Add PropertyCompositeView tests --- src/style_manager/model/Property.js | 2 +- src/style_manager/view/PropertiesView.js | 16 +- .../view/PropertyCompositeView.js | 32 +-- src/style_manager/view/PropertyView.js | 27 ++- test/specs/style_manager/main.js | 5 +- .../view/PropertyCompositeView.js | 213 ++++++++++++++++++ 6 files changed, 260 insertions(+), 35 deletions(-) create mode 100644 test/specs/style_manager/view/PropertyCompositeView.js diff --git a/src/style_manager/model/Property.js b/src/style_manager/model/Property.js index cf99e98c9..fdf1d95d2 100644 --- a/src/style_manager/model/Property.js +++ b/src/style_manager/model/Property.js @@ -11,7 +11,7 @@ define(['backbone'], unit: '', defaults: '', info: '', - value: '', //Selected value of the property (?) + value: '', icon: '', preview: false, functionName: '', diff --git a/src/style_manager/view/PropertiesView.js b/src/style_manager/view/PropertiesView.js index bce404521..2735adff7 100644 --- a/src/style_manager/view/PropertiesView.js +++ b/src/style_manager/view/PropertiesView.js @@ -41,14 +41,14 @@ define(['backbone','./PropertyView', './PropertyIntegerView', './PropertyRadioVi } var view = new objView({ - model : model, - name : model.get('name'), - id : this.pfx + model.get('property'), - target : this.target, - propTarget : this.propTarget, - onChange : this.onChange, - onInputRender : this.onInputRender, - config : this.config, + model: model, + name: model.get('name'), + id: this.pfx + model.get('property'), + target: this.target, + propTarget: this.propTarget, + onChange: this.onChange, + onInputRender: this.onInputRender, + config: this.config, }); if(model.get('type') != 'composite'){ diff --git a/src/style_manager/view/PropertyCompositeView.js b/src/style_manager/view/PropertyCompositeView.js index 310015858..1437b5f49 100644 --- a/src/style_manager/view/PropertyCompositeView.js +++ b/src/style_manager/view/PropertyCompositeView.js @@ -10,8 +10,8 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html initialize: function(o) { PropertyView.prototype.initialize.apply(this, arguments); _.bindAll(this, 'build'); - this.config = o.config; - this.className = this.className + ' '+ this.pfx +'composite'; + this.config = o.config || {}; + this.className = this.className + ' '+ this.pfx +'composite'; }, /** @@ -26,8 +26,8 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html this.$input = $('', {value: 0, type: 'hidden' }); if(!this.props){ - var Properties = require('./../model/Properties'); - this.props = new Properties(props); + var Properties = require('./../model/Properties'); + this.props = new Properties(props); this.model.set('properties', this.props); } @@ -36,13 +36,14 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html this.props.each(function(prop, index){ prop.set('doNotStyle', true); }); + //Not yet supported nested composite this.props.each(function(prop, index){ if(prop && prop.get('type') == 'composite'){ this.props.remove(prop); - console.warn(prop.get('property')+' of type composite not yet allowed.'); + console.warn('Nested composite types not yet allowed.'); } - },this); + }, this); var PropertiesView = require('./PropertiesView'); var that = this; @@ -51,17 +52,23 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html collection : this.props, target : this.target, propTarget : this.propTarget, + // On any change made to children I need to update composite value onChange : function(el, model){ var result = that.build(el, model); that.model.set('value', result); }, - onInputRender : function(property, mIndex){ - var value = that.valueOnIndex(mIndex, property.model); - property.setValue(value); - }, + // Each child property will receive a full composite string, eg. '0px 0px 10px 0px' + // I need to extract from that string the corresponding one to that property. customValue : function(property, mIndex){ return that.valueOnIndex(mIndex, property.model); }, + // setValue is already invoked by renderInput(). + // TODO: Remove definitively after all tests + /* + onInputRender : function(property, mIndex){ + var value = that.valueOnIndex(mIndex, property.model); + property.setValue(value); + },*/ }); this.$props = propsView.render().$el; this.$el.find('#'+ this.pfx +'input-holder').html(this.$props); @@ -94,7 +101,6 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html var a = this.getComponentValue().split(' '); if(a.length && a[index]){ result = a[index]; - //Check for function type values if(model && model.get('functionName')){ var v = this.fetchFromFunction(result); if(v) @@ -108,19 +114,19 @@ define(['backbone','./PropertyView', 'text!./../templates/propertyComposite.html * Build composite value * @param Object Selected element * @param Object Property model - * + * @todo alias getValueForTarget? * @return string * */ build: function(selectedEl, propertyModel){ var result = ''; this.model.get('properties').each(function(prop){ + //TODO v = prop.getValueForTarget(); -> functionName inside?!? var v = (prop.get('value') || prop.get('defaults')) + prop.get('unit'), func = prop.get('functionName'); if(func) v = func + '(' + v + ')'; result += v + ' '; }); - //Remove also the last white space return result.replace(/ +$/,''); }, diff --git a/src/style_manager/view/PropertyView.js b/src/style_manager/view/PropertyView.js index a04ecda30..98a46ea75 100644 --- a/src/style_manager/view/PropertyView.js +++ b/src/style_manager/view/PropertyView.js @@ -32,6 +32,16 @@ define(['backbone', 'text!./../templates/propertyLabel.html', 'text!./../templat this.listenTo( this.model ,'change:value', this.valueChanged); }, + /** + * Returns selected target which should have 'style' property + * @return {Model|null} + */ + getTarget: function(){ + if(this.selectedComponent) + return this.selectedComponent; + return this.propTarget ? this.propTarget.model : null; + }, + /** * Fired when the input value is updated */ @@ -136,7 +146,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html', 'text!./../templat if(this.$input) this.setValue(mVal); - if(!this.selectedComponent) + if(!this.getTarget()) return; // Check if component is allowed to be styled @@ -149,21 +159,20 @@ define(['backbone', 'text!./../templates/propertyLabel.html', 'text!./../templat value = this.func + '(' + value + ')'; if( !this.model.get('doNotStyle') ){ - var componentCss = _.clone( this.selectedComponent.get('style') ); + var componentCss = _.clone( this.getTarget().get('style') ); if(value) componentCss[this.property] = value; else delete componentCss[this.property]; - this.selectedComponent.set('style', componentCss, { avoidStore : avSt}); + this.getTarget().set('style', componentCss, { avoidStore : avSt}); if(this.helperComponent) this.helperComponent.set('style', componentCss, { avoidStore : avSt}); } - this.selectedValue = value; if(this.onChange && typeof this.onChange === "function"){ - this.onChange(this.selectedComponent, this.model); + this.onChange(this.getTarget(), this.model); } }, @@ -172,9 +181,7 @@ define(['backbone', 'text!./../templates/propertyLabel.html', 'text!./../templat * @return {Boolean} */ isTargetStylable: function(){ - if(!this.selectedComponent) - return; - var stylable = this.selectedComponent.get('stylable'); + var stylable = this.getTarget().get('stylable'); // Stylable could also be an array indicating with which property // the target could be styled if(stylable instanceof Array) @@ -251,10 +258,6 @@ define(['backbone', 'text!./../templates/propertyLabel.html', 'text!./../templat * */ renderInputRequest: function(){ this.renderInput(); - if(this.onInputRender && typeof this.onInputRender === "function"){ - var index = this.model.collection.indexOf(this.model); - this.onInputRender(this, index); - } }, /** diff --git a/test/specs/style_manager/main.js b/test/specs/style_manager/main.js index 843831d30..ad36484ac 100644 --- a/test/specs/style_manager/main.js +++ b/test/specs/style_manager/main.js @@ -10,6 +10,7 @@ define([ modulePath + '/view/PropertyRadioView', modulePath + '/view/PropertyIntegerView', modulePath + '/view/PropertyColorView', + modulePath + '/view/PropertyCompositeView', ], function( StyleManager, @@ -20,7 +21,8 @@ define([ PropertySelectView, PropertyRadioView, PropertyIntegerView, - PropertyColorView + PropertyColorView, + PropertyCompositeView ) { describe('StyleManager', function() { @@ -194,6 +196,7 @@ define([ PropertyRadioView.run(); PropertyIntegerView.run(); PropertyColorView.run(); + PropertyCompositeView.run(); }); diff --git a/test/specs/style_manager/view/PropertyCompositeView.js b/test/specs/style_manager/view/PropertyCompositeView.js new file mode 100644 index 000000000..3bd65d986 --- /dev/null +++ b/test/specs/style_manager/view/PropertyCompositeView.js @@ -0,0 +1,213 @@ +var path = 'StyleManager/view/'; +define([path + 'PropertyCompositeView', 'StyleManager/model/Property', 'DomComponents/model/Component'], + function(PropertyCompositeView, Property, Component) { + + return { + run : function(){ + + describe('PropertyCompositeView', function() { + + var component; + var $fixtures; + var $fixture; + var target; + var model; + var view; + var propName = 'testprop'; + var propValue = 'test1value'; + var defValue = 'test2value'; + var properties = [ + {property: 'subprop1'}, + { + type: 'integer', + property: 'subprop2', + defaults: 0, + units: ['%', 'px'] + }, + { + type: 'select', + property: 'subprop3', + defaults: 'val2', + list: [ + {value:'val1'}, + {value:'val2'}, + {value:'val3'}, + ] + }, + ]; + + before(function () { + $fixtures = $("#fixtures"); + $fixture = $('
'); + }); + + beforeEach(function () { + target = new Component(); + component = new Component(); + target.model = component; + model = new Property({ + type: 'composite', + property: propName, + properties: properties + }); + view = new PropertyCompositeView({ + model: model + }); + $fixture.empty().appendTo($fixtures); + $fixture.html(view.render().el); + }); + + afterEach(function () { + //view.remove(); // strange errors ??? + }); + + after(function () { + $fixture.remove(); + delete component; + delete view; + delete model; + }); + + it('Rendered correctly', function() { + var prop = view.el; + $fixture.get(0).querySelector('.property').should.be.ok; + prop.querySelector('.label').should.be.ok; + prop.querySelector('.field').should.be.ok; + }); + + it('Properties rendered', function() { + var prop = view.el; + prop.querySelector('.properties').should.be.ok; + }); + + it('Properties rendered correctly', function() { + var children = view.el.querySelector('.properties').children; + children.length.should.equal(properties.length + 1); + children[0].id.should.equal(properties[0].property); + children[1].id.should.equal(properties[1].property); + children[2].id.should.equal(properties[2].property); + }); + + it('Props should exist', function() { + view.$props.should.be.ok; + }); + + it('Input value is empty', function() { + view.model.get('value').should.be.empty; + }); + + it('Update input on value change', function() { + view.model.set('value', propValue); + view.$input.val().should.equal(propValue); + }); + + describe('With target setted', function() { + + var prop2Val; + var prop3Val; + var prop2Unit; + var finalResult; + var $prop1; + var $prop2; + var $prop3; + + beforeEach(function () { + model = new Property({ + type: 'composite', + property: propName, + properties: properties + }); + view = new PropertyCompositeView({ + model: model, + propTarget: target + }); + $fixture.empty().appendTo($fixtures); + $fixture.html(view.render().el); + prop3Val = properties[2].list[2].value; + prop2Val = properties[1].defaults; + prop2Unit = properties[1].units[0]; + finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val; + $prop1 = view.$props.find('#' + properties[0].property + ' input'); + $prop2 = view.$props.find('#' + properties[1].property + ' input'); + $prop3 = view.$props.find('#' + properties[2].property + ' select'); + }); + + it('Update model on input change', function() { + var $prop1 = view.$props.find('#' + properties[0].property + ' input'); + var $prop2 = view.$props.find('#' + properties[1].property + ' input'); + var $prop3 = view.$props.find('#' + properties[2].property + ' select'); + var prop3Val = properties[2].list[2].value; + var prop2Val = properties[1].defaults; + var prop2Unit = properties[1].units[0]; + var finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val; + $prop1.val(propValue).trigger('change'); + $prop3.val(prop3Val).trigger('change'); + view.model.get('value').should.equal(finalResult); + }); + + it('Update value on models change', function() { + view.model.get('properties').at(0).set('value', propValue); + view.model.get('properties').at(2).set('value', prop3Val); + view.model.get('value').should.equal(finalResult); + }); + + it('Update target on value change', function() { + var $prop1 = view.$props.find('#' + properties[0].property + ' input'); + $prop1.val(propValue).trigger('change'); + var compStyle = view.getTarget().get('style'); + var assertStyle = {}; + assertStyle[propName] = propValue + ' 0% val2'; + compStyle.should.deep.equal(assertStyle); + }); + + it('Update value and input on target swap', function() { + var style = {}; + style[propName] = finalResult; + component.set('style', style); + view.propTarget.trigger('update'); + $prop1.val().should.equal(propValue); + $prop3.val().should.equal(prop3Val); + }); + + it('Update value after multiple swaps', function() { + var style = {}; + style[propName] = finalResult; + component.set('style', style); + view.propTarget.trigger('update'); + style[propName] = propValue + '2 ' + prop2Val + '2' + prop2Unit +' ' + 'val1'; + component.set('style', style); + view.propTarget.trigger('update'); + $prop1.val().should.equal(propValue + '2'); + $prop2.val().should.equal('2'); + $prop3.val().should.equal('val1'); + }); + + }) + + describe('Init property', function() { + + beforeEach(function () { + model = new Property({ + type: 'composite', + property: propName, + properties: properties, + defaults: defValue, + }); + view = new PropertyCompositeView({ + model: model + }); + $fixture.empty().appendTo($fixtures); + $fixture.html(view.render().el); + }); + + it('Value as default', function() { + view.model.get('value').should.equal(defValue); + }); + + }); + + }); + } + }; + +}); \ No newline at end of file