SOURCE CODE: Uize.Test.Uize.Widget.HtmltCompiler (view docs)

/*______________
|       ______  |   U I Z E    J A V A S C R I P T    F R A M E W O R K
|     /      /  |   ---------------------------------------------------
|    /    O /   |    MODULE : Uize.Test.Uize.Widget.HtmltCompiler Class
|   /    / /    |
|  /    / /  /| |    ONLINE : http://uize.com
| /____/ /__/_| | COPYRIGHT : (c)2015-2016 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://uize.com/license.html
*/

/* Module Meta Data
  type: Test
  importance: 3
  codeCompleteness: 100
  docCompleteness: 100
*/

/*?
  Introduction
    The =Uize.Test.Uize.Widget.HtmltCompiler= module defines a suite of unit tests for the =Uize.Widget.HtmltCompiler= module.

    *DEVELOPERS:* `Chris van Rensburg`
*/

/* TODO:
  - test value bindings to form elements
    - select (not yet supported)
*/

Uize.module ({
  name:'Uize.Test.Uize.Widget.HtmltCompiler',
  builder:function () {
    'use strict';

    /*** Utility Methods ***/
      function _htmlBindingsTest (_title,_widgetClassFeatures,_htmltSource,_expected) {
        return {
          title:_title,
          test:function () {
            var
              _WidgetClass = Uize.Widget.subclass ({
                mixins:Uize.Widget.mHtmlBindings,
                instanceMethods:{
                  cssClass:function (_class) {return 'Widget-' + _class}
                }
              }).declare (_widgetClassFeatures),
              _template = Uize.Widget.HtmltCompiler.compile (
                _htmltSource,
                {widgetClass:_WidgetClass}
              ),
              _widgetInstance = _WidgetClass ({idPrefix:'widget'})
            ;
            return this.expect (
              _expected,
              _template.call (_widgetInstance,_widgetInstance.get ())
            );
          }
        };
      }

    return Uize.Test.resolve ({
      title:'Uize.Widget.HtmltCompiler Module Test',
      test:[
        Uize.Test.requiredModulesTest ([
          'Uize.Widget',
          'Uize.Widget.mHtmlBindings',
          'Uize.Widget.HtmltCompiler',
          'Uize.Util.Html.Encode',
          'Uize.Parse.Xml.NodeList'
        ]),
        Uize.Test.staticMethodsTest ([
          ['Uize.Widget.HtmltCompiler.compile',[
            {
              title:'The values of id attributes are prefixed with the idPrefix of the widget',
              test:[
                _htmlBindingsTest (
                  'If the root node does not contain an id attribute, one is added and its value is set to the idPrefix of the widget',
                  {},
                  '
', '
' ), _htmlBindingsTest ( 'If the root node contains an id attribute with an empty value, its value is set to the idPrefix of the widget', {}, '
', '
' ), _htmlBindingsTest ( 'If the actual root node in the DOM has an id attribute that is not empty, then that node is not regarded as the widget root node and its existing id is preserved', {}, '
', '
' ), _htmlBindingsTest ( 'The values of all id attributes are prefixed with the idPrefix of the widget', {}, '
' + '
DIV 1
' + '
DIV 2
' + '
' + '
' + 'A SPAN' + '
' + '' + '' + '' + '
' + '
', '
' + '
DIV 1
' + '
DIV 2
' + '
' + '
' + 'A SPAN' + '
' + '' + '' + '' + '
' + '
' ) ] }, { title:'The values of class attributes are expanded by calling the cssClass method of the widget', test:[ _htmlBindingsTest ( 'The values of class attributes for multiple nodes will be expanded', {}, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the values of class attributes contain multiple classes, each class will be expanded in separate calls to the cssClass method of the widget', {}, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ) ] }, { title:'The values of state properties can be bound to nodes to replace their contents', test:[ _htmlBindingsTest ( 'A state property can be bound to replace the contents of the root node', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'' } }, '
', '
bar
' ), _htmlBindingsTest ( 'A state property can be bound to replace the contents of a child node', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'foo' } }, '
' + '
' + '
', '
' + '
bar
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to replace the contents of multiple nodes', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:['foo1','foo2','foo3'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
bar
' + '
bar
' + '
' + '
bar
' + '
' + '
' ), _htmlBindingsTest ( 'The value of a state property is HTML encoded when it is bound to replace the contents of a node', { stateProperties:{ foo:{value:'Characters that need to be HTML-encoded: <&"'} }, htmlBindings:{ foo:'foo' } }, '
' + '
' + '
', '
' + '
Characters that need to be HTML-encoded: <&"
' + '
' ) ] }, { title:'The values of state properties can be bound to nodes to replace their inner HTML', test:[ _htmlBindingsTest ( 'A state property can be bound to replace the inner HTML of the root node', { stateProperties:{ foo:{value:'
bar
'} }, htmlBindings:{ foo:':innerHTML' } }, '
', '
bar
' ), _htmlBindingsTest ( 'A state property can be bound to replace the inner HTML of a child node', { stateProperties:{ foo:{value:'
bar
'} }, htmlBindings:{ foo:'foo:innerHTML' } }, '
' + '
' + '
', '
' + '
bar
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to replace the inner HTML of multiple nodes', { stateProperties:{ foo:{value:'
bar
'} }, htmlBindings:{ foo:['foo1:innerHTML','foo2:innerHTML','foo3:innerHTML'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
bar
' + '
bar
' + '
' + '
bar
' + '
' + '
' ), _htmlBindingsTest ( 'Replacement of the inner HTML of nodes is one pass only, so the replacement HTML content is not subsequently processed or affected by bindings', { stateProperties:{ foo:{ value:'
bar
' } }, htmlBindings:{ foo:['foo1:innerHTML','foo2:innerHTML'] } }, '
' + '
' + '
' + '
', '
' + '
' + '
bar
' + '
' + '
' + '
bar
' + '
' + '
' ) ] }, { title:'The values of state properties can be bound to various attributes of nodes', test:[ _htmlBindingsTest ( 'A state property can be bound to an attribute of the root node', { stateProperties:{ linkHref:{value:'http://uize.com'} }, htmlBindings:{ linkHref:':@href' } }, 'uize.com', 'uize.com' ), _htmlBindingsTest ( 'A state property can be bound to an attribute of a child node', { stateProperties:{ linkHref:{value:'http://uize.com'} }, htmlBindings:{ linkHref:'foo:@href' } }, '
' + 'uize.com' + '
', '
' + 'uize.com' + '
' ), _htmlBindingsTest ( 'A state property can be bound to an attribute of multiple nodes', { stateProperties:{ linkHref:{value:'http://uize.com'} }, htmlBindings:{ linkHref:['foo1:@href','foo2:@href','foo3:@href'] } }, '
' + 'uize.com' + 'uize.com' + '
' + 'uize.com' + '
' + '
', '
' + 'uize.com' + 'uize.com' + '
' + 'uize.com' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to multiple different attributes of the same node', { stateProperties:{ linkHref:{value:'http://uize.com'}, linkTitle:{value:'The UIZE Web site'}, linkTarget:{value:'_blank'} }, htmlBindings:{ linkHref:'foo:@href', linkTitle:'foo:@title', linkTarget:'foo:@target' } }, '
' + 'uize.com' + '
', '
' + 'uize.com' + '
' ), _htmlBindingsTest ( 'If a state property is bound to an attribute of a node and that attribute already has a value specified, then the attribute\'s initial value is ignored and is replaced by the value from the state property binding', { stateProperties:{ linkHref:{value:'http://uize.com'} }, htmlBindings:{ linkHref:'foo:@href', } }, '
' + 'uize.com' + '
', '
' + 'uize.com' + '
' ), _htmlBindingsTest ( 'If a state property is bound to an attribute of a node and that node already contains other attributes, those other attributes for which there are no bindings are retained', { stateProperties:{ linkHref:{value:'http://uize.com'} }, htmlBindings:{ linkHref:'foo:@href', } }, '
' + 'uize.com' + '
', '
' + 'uize.com' + '
' ), _htmlBindingsTest ( 'The value of a state property is HTML encoded when it is bound to an attribute of a node', { stateProperties:{ linkTitle:{value:'The "" Web site'} }, htmlBindings:{ linkTitle:'foo:@title', } }, '
' + 'uize.com' + '
', '
' + 'uize.com' + '
' ) ] }, { title:'The values of state properties can be bound to various types of form elements', test:[ { title:'A state property can be bound to text input nodes', test:[ _htmlBindingsTest ( 'A state property can be bound to a single text input node', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to multiple text input nodes', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:['foo1','foo2','foo3'] } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to multiple respective text input nodes', { stateProperties:{ foo1:{value:'bar1'}, foo2:{value:'bar2'}, foo3:{value:'bar3'} }, htmlBindings:{ foo1:'foo1', foo2:'foo2', foo3:'foo3' } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'The value of a state property is HTML encoded when it is bound to a text input node', { stateProperties:{ foo:{value:'Characters that need to be HTML-encoded: <&"'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'If a text input node to which a state property has been bound already has a value attribute, its initial value is ignored and is replaced by the value from the state property binding', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ) ] }, { title:'A state property can be bound to textarea nodes', test:[ _htmlBindingsTest ( 'A state property can be bound to a single textarea node', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to multiple textarea nodes', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:['foo1','foo2','foo3'] } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to multiple respective text input nodes', { stateProperties:{ foo1:{value:'bar1'}, foo2:{value:'bar2'}, foo3:{value:'bar3'} }, htmlBindings:{ foo1:'foo1', foo2:'foo2', foo3:'foo3' } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'The value of a state property is HTML encoded when it is bound to a textarea node', { stateProperties:{ foo:{value:'Characters that need to be HTML-encoded: <&"'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'If a textarea node to which a state property has been bound already contains content, this content is ignored and is replaced by the value from the state property binding', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ) ] }, { title:'A state property can be bound to checkbox input nodes', test:[ _htmlBindingsTest ( 'A state property can be bound to a single checkbox input node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to multiple checkbox input nodes', { stateProperties:{ foo:{value:'bar'} }, htmlBindings:{ foo:['foo1','foo2','foo3'] } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to multiple respective checkbox input nodes', { stateProperties:{ foo1:{value:true}, foo2:{value:false}, foo3:{value:true} }, htmlBindings:{ foo1:'foo1', foo2:'foo2', foo3:'foo3' } }, '
' + '' + '' + '
' + '' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'When a state property is bound to a checkbox input node and the state property\'s value is false, the checked attribute will be absent', { stateProperties:{ foo:{value:false} }, htmlBindings:{ foo:'foo' } }, '
' + '' + '
', '
' + '' + '
' ) ] } ] }, { title:'The values of state properties can be bound to various style properties of nodes', test:[ _htmlBindingsTest ( 'A state property can be bound to a style property of the root node', { stateProperties:{ foo:{value:'15px'} }, htmlBindings:{ foo:':style.width' } }, '
', '
' ), _htmlBindingsTest ( 'A state property can be bound to a style property of a child node', { stateProperties:{ foo:{value:'15px'} }, htmlBindings:{ foo:'foo:style.width' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to a style property of multiple nodes', { stateProperties:{ foo:{value:'15px'} }, htmlBindings:{ foo:['foo1:style.width','foo2:style.width','foo3:style.width'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to multiple different style properties of the same node', { stateProperties:{ left:{value:'5px'}, top:{value:'6px'}, right:{value:'7px'}, bottom:{value:'8px'} }, htmlBindings:{ left:'foo:style.left', top:'foo:style.top', right:'foo:style.right', bottom:'foo:style.bottom' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'When the name of a style property in a style binding uses the DOM camelCase naming convention, it is resolved to the appropriate hyphenated form for CSS style properties', { stateProperties:{ foo:{value:10} }, htmlBindings:{ foo:'foo:style.borderLeftWidth' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'When the name of a style property in a style binding uses the appropriate hyphenated form for CSS style properties, it is used as is', { stateProperties:{ foo:{value:10} }, htmlBindings:{ foo:'foo:style.border-left-width' } }, '
' + '
' + '
', '
' + '
' + '
' ), /* _htmlBindingsTest ( 'If state properties are bound to style properties of a node and those style properties already have values specified, then the style properties\' initial values are ignored and are replaced by the values from the state property bindings', { stateProperties:{ width:{value:'100%'}, height:{value:'100%'} }, htmlBindings:{ width:'foo:style.width', height:'foo:style.height' } }, '
' + '
' + '
', '
' + '
' + '
' ), */ _htmlBindingsTest ( 'If a state property is bound to a style property of a node and that node already contains other style properties, those other style properties for which there are no bindings are retained', { stateProperties:{ foo:{value:'100%'} }, htmlBindings:{ foo:'foo:style.width' } }, '
' + '
' + '
', '
' + '
' + '
' ), { title:'Number type values for state properties are resolved to string values, with the "px" being appended in certain cases', test:[ _htmlBindingsTest ( 'When a state property with a number type value is bound to a style property that supports pixel units, the "px" suffix is appended by the binding', { stateProperties:{ foo:{value:100} }, htmlBindings:{ foo:[ ':style.width', ':style.height', ':style.left', ':style.top', ':style.right', ':style.bottom', ':style.borderWidth' ] } }, '
', '
' ), _htmlBindingsTest ( 'When a state property with a string type value is bound to a style property that supports pixel units, the "px" suffix is not appended by the binding', { stateProperties:{ foo:{value:'100%'} }, htmlBindings:{ foo:[ ':style.width', ':style.height', ':style.left', ':style.top', ':style.right', ':style.bottom', ':style.borderWidth' ] } }, '
', '
' ), _htmlBindingsTest ( 'When a state property with a number type value is bound to a style property that does\'t support pixel units, the "px" suffix is not appended by the binding', { stateProperties:{ foo:{value:1} }, htmlBindings:{ foo:[ ':style.zIndex', ':style.opacity' ] } }, '
', '
' ) ] } ] }, { title:'The values of state properties can be bound to the display of nodes using the "?" binding type', test:[ _htmlBindingsTest ( 'A state property can be bound to the display of the root node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:':?' } }, '
', '
' ), _htmlBindingsTest ( 'A state property can be bound to the display of a child node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:'foo:?' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to the display of multiple nodes', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:['foo1:?','foo2:?','foo3:?'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to the display of multiple respective nodes', { stateProperties:{ foo1:{value:true}, foo2:{value:false}, foo3:{value:true} }, htmlBindings:{ foo1:'foo1:?', foo2:'foo2:?', foo3:'foo3:?' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to the display of a node is truthy, then the style "display:block" will be used for displaying the node', { stateProperties:{ foo1:{value:true}, foo2:{value:1}, foo3:{value:'foo'}, foo4:{value:[]}, foo5:{value:{}} }, htmlBindings:{ foo1:'foo1:?', foo2:'foo2:?', foo3:'foo3:?', foo4:'foo4:?', foo5:'foo5:?' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to the display of a node is falsy, then the style "display:none" will be used for displaying the node', { stateProperties:{ foo1:{value:false}, foo2:{value:0}, foo3:{value:''}, foo4:{value:null}, foo5:{value:undefined} }, htmlBindings:{ foo1:'foo1:?', foo2:'foo2:?', foo3:'foo3:?', foo4:'foo4:?', foo5:'foo5:?' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '' + '' + '' + '' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to the display of a node together with other style bindings', { stateProperties:{ display:{value:true}, width:{value:'80%'}, height:{value:'100%'} }, htmlBindings:{ display:':?', width:':style.width', height:':style.height' } }, '
', '
' ) ] }, { title:'The values of state properties can be bound to show nodes using the "show" binding type', test:[ _htmlBindingsTest ( 'A state property can be bound to show the root node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:':show' } }, '
', '
' ), _htmlBindingsTest ( 'A state property can be bound to show a child node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:'foo:show' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to show multiple nodes', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:['foo1:show','foo2:show','foo3:show'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to the display of multiple respective nodes', { stateProperties:{ foo1:{value:true}, foo2:{value:false}, foo3:{value:true} }, htmlBindings:{ foo1:'foo1:show', foo2:'foo2:show', foo3:'foo3:show' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to show a node is truthy, then the style "display:" will be used for showing the node', { stateProperties:{ foo1:{value:true}, foo2:{value:1}, foo3:{value:'foo'}, foo4:{value:[]}, foo5:{value:{}} }, htmlBindings:{ foo1:'foo1:show', foo2:'foo2:show', foo3:'foo3:show', foo4:'foo4:show', foo5:'foo5:show' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to show a node is falsy, then the style "display:none" will be used for not showing the node', { stateProperties:{ foo1:{value:false}, foo2:{value:0}, foo3:{value:''}, foo4:{value:null}, foo5:{value:undefined} }, htmlBindings:{ foo1:'foo1:show', foo2:'foo2:show', foo3:'foo3:show', foo4:'foo4:show', foo5:'foo5:show' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '' + '' + '' + '' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to show a node together with other style bindings', { stateProperties:{ show:{value:true}, width:{value:'80%'}, height:{value:'100%'} }, htmlBindings:{ show:':show', width:':style.width', height:':style.height' } }, '
', '
' ) ] }, { title:'The values of state properties can be bound to hide nodes using the "hide" binding type', test:[ _htmlBindingsTest ( 'A state property can be bound to hide the root node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:':hide' } }, '
', '' ), _htmlBindingsTest ( 'A state property can be bound to hide a child node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:'foo:hide' } }, '
' + '
' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'A state property can be bound to hide multiple nodes', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:['foo1:hide','foo2:hide','foo3:hide'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '' + '' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'Multiple state properties can be bound to the display of multiple respective nodes', { stateProperties:{ foo1:{value:true}, foo2:{value:false}, foo3:{value:true} }, htmlBindings:{ foo1:'foo1:hide', foo2:'foo2:hide', foo3:'foo3:hide' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '' + '
' + '
' + '' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to hide a node is truthy, then the style "display:none" will be used for hideing the node', { stateProperties:{ foo1:{value:true}, foo2:{value:1}, foo3:{value:'foo'}, foo4:{value:[]}, foo5:{value:{}} }, htmlBindings:{ foo1:'foo1:hide', foo2:'foo2:hide', foo3:'foo3:hide', foo4:'foo4:hide', foo5:'foo5:hide' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '' + '' + '' + '' + '' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to hide a node is falsy, then the style "display:" will be used for not hideing the node', { stateProperties:{ foo1:{value:false}, foo2:{value:0}, foo3:{value:''}, foo4:{value:null}, foo5:{value:undefined} }, htmlBindings:{ foo1:'foo1:hide', foo2:'foo2:hide', foo3:'foo3:hide', foo4:'foo4:hide', foo5:'foo5:hide' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'A state property can be bound to hide a node together with other style bindings', { stateProperties:{ hide:{value:true}, width:{value:'80%'}, height:{value:'100%'} }, htmlBindings:{ hide:':hide', width:':style.width', height:':style.height' } }, '
', '' ) ] }, _htmlBindingsTest ( 'There can be multiple different types of bindings to the same node', { stateProperties:{ prop1:{value:'someClass'}, title:{value:'Some Title'}, text:{value:'Some Text'}, color:{value:'#ccc'}, dispaly:{value:false} }, htmlBindings:{ prop1:'foo:className', title:['foo:@title','foo:@alt'], text:'foo', color:'foo:style.color', display:'foo:?' } }, '
' + '' + '
', '
' + '' + '
' ), _htmlBindingsTest ( 'The className binding type is remapped to the "@class" binding type (i.e. a binding to the class attribute)', { stateProperties:{ class1:{value:'enabled'}, class2:{value:'selected'} }, htmlBindings:{ class1:[ ':className', 'foo1:className' ], class2:[ 'foo2:className', 'foo3:className' ] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), { title:'A special "readOnly" binding type is supported', test:[ _htmlBindingsTest ( 'A "readOnly" type binding can be applied to the root node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:':readOnly' } }, '
', '
' ), _htmlBindingsTest ( 'A "readOnly" type binding can be applied to a child node', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:'foo:readOnly' } }, '
' + '
' + '
', '
' + '
' + '
' ), _htmlBindingsTest ( 'A "readOnly" type binding can be applied to multiple nodes', { stateProperties:{ foo:{value:true} }, htmlBindings:{ foo:['foo1:readOnly','foo2:readOnly','foo3:readOnly'] } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'Multiple "readOnly" type bindings can be applied to multiple respective nodes', { stateProperties:{ foo1:{value:true}, foo2:{value:false}, foo3:{value:true} }, htmlBindings:{ foo1:'foo1:readOnly', foo2:'foo2:readOnly', foo3:'foo3:readOnly' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to a node using a "readOnly" type binding is truthy, then the "readonly" attribute is added to the node', { stateProperties:{ foo1:{value:true}, foo2:{value:1}, foo3:{value:'foo'}, foo4:{value:[]}, foo5:{value:{}} }, htmlBindings:{ foo1:'foo1:readOnly', foo2:'foo2:readOnly', foo3:'foo3:readOnly', foo4:'foo4:readOnly', foo5:'foo5:readOnly' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ), _htmlBindingsTest ( 'When the value of a state property that is bound to a node using a "readOnly" type binding is falsy, then the "readonly" attribute is not added to the node', { stateProperties:{ foo1:{value:false}, foo2:{value:0}, foo3:{value:''}, foo4:{value:null}, foo5:{value:undefined} }, htmlBindings:{ foo1:'foo1:readOnly', foo2:'foo2:readOnly', foo3:'foo3:readOnly', foo4:'foo4:readOnly', foo5:'foo5:readOnly' } }, '
' + '
' + '
' + '
' + '
' + '
' + '
', '
' + '
' + '
' + '
' + '
' + '
' + '
' ) ] }, { title:'HTML for child widgets can be inserted using the special "child" tag', test:[ { title:'HTML for a child widget can be inserted using the "child" tag', test:function () { var _ChildWidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'ChildWidget-' + _class} } }), _WidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'Widget-' + _class} }, omegastructor:function () { this.addChild ('foo',_ChildWidgetClass); } }), _template = Uize.Widget.HtmltCompiler.compile ( '
' + '' + '
', {widgetClass:_WidgetClass} ) ; _ChildWidgetClass.set ({ html:{ process:Uize.Widget.HtmltCompiler.compile ( '
', {widgetClass:_ChildWidgetClass} ) } }); var _widgetInstance = _WidgetClass ({idPrefix:'widget'}); return this.expect ( '
' + '
' + '
', _template.call (_widgetInstance,_widgetInstance.get ()) ); } }, { title:'When the value of the "name" attribute in a "child" tag does not correspond to the name of an existing child widget, then no HTML content is inserted for the child', test:function () { var _WidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'Widget-' + _class} } }), _template = Uize.Widget.HtmltCompiler.compile ( '
' + '' + '
', {widgetClass:_WidgetClass} ), _widgetInstance = _WidgetClass ({idPrefix:'widget'}) ; return this.expect ( '
', _template.call (_widgetInstance,_widgetInstance.get ()) ); } }, { title:'When inserting HTML for a child widget using the "child" tag, values for the state properties of the child widget can be specified in attributes of the child tag that are named the same as the state properties', test:function () { var _ChildWidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'ChildWidget-' + _class} }, stateProperties:{ ptop1:{}, prop2:{} }, htmlBindings:{ prop1:':@title', prop2:':value' } }), _WidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'Widget-' + _class} }, omegastructor:function () { this.addChild ('foo',_ChildWidgetClass); } }), _template = Uize.Widget.HtmltCompiler.compile ( '
' + '' + '
', {widgetClass:_WidgetClass} ) ; _ChildWidgetClass.set ({ html:{ process:Uize.Widget.HtmltCompiler.compile ( '
', {widgetClass:_ChildWidgetClass} ) } }); var _widgetInstance = _WidgetClass ({idPrefix:'widget'}); return this.expect ( '
' + '
fooProp2
' + '
', _template.call (_widgetInstance,_widgetInstance.get ()) ); } }, { title:'When a "child" tag contains a "class" or "extraClasses" attribute, the value of this attribute is split into separate class namespacer expressions (namespaced to the parent widget) and the concatenation of these is passed as the value of the "extraClasses" state property of the child widget', test:function () { var _ChildWidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'ChildWidget-' + _class} }, stateProperties:{ extraClasses:{value:''} }, htmlBindings:{ extraClasses:':@class' } }), _WidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'Widget-' + _class} }, omegastructor:function () { this.addChild ('foo',_ChildWidgetClass); this.addChild ('bar',_ChildWidgetClass); } }), _template = Uize.Widget.HtmltCompiler.compile ( '
' + '' + '' + '
', {widgetClass:_WidgetClass} ) ; _ChildWidgetClass.set ({ html:{ process:Uize.Widget.HtmltCompiler.compile ( '
', {widgetClass:_ChildWidgetClass} ) } }); var _widgetInstance = _WidgetClass ({idPrefix:'widget'}); return this.expect ( '
' + '
' + '
' + '
', _template.call (_widgetInstance,_widgetInstance.get ()) ); } }, { title:'HTML for multiple child widget instances can be inserted in different places in the template, using multiple "child" tags', test:function () { var _ChildWidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'ChildWidget-' + _class} }, stateProperties:{ ptop1:{}, prop2:{} }, htmlBindings:{ prop1:':@title', prop2:':value' } }), _WidgetClass = Uize.Widget.subclass ({ mixins:Uize.Widget.mHtmlBindings, instanceMethods:{ cssClass:function (_class) {return 'Widget-' + _class} }, omegastructor:function () { this.addChild ('foo',_ChildWidgetClass); this.addChild ('bar',_ChildWidgetClass); this.addChild ('baz',_ChildWidgetClass); } }), _template = Uize.Widget.HtmltCompiler.compile ( '
' + '' + '
' + '' + '
' + '' + '
' + '
' + '
', {widgetClass:_WidgetClass} ) ; _ChildWidgetClass.set ({ html:{ process:Uize.Widget.HtmltCompiler.compile ( '
', {widgetClass:_ChildWidgetClass} ) } }); var _widgetInstance = _WidgetClass ({idPrefix:'widget'}); return this.expect ( '
' + '
' + 'fooProp2' + '
' + '
' + '
' + 'barProp2' + '
' + '
' + '
' + 'bazProp2' + '
' + '
' + '
' + '
', _template.call (_widgetInstance,_widgetInstance.get ()) ); } } ] } ]] ]) ] }); } });