/*______________
| ______ | 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.Class.mChildObjectBindings Class
| / / / |
| / / / /| | ONLINE : http://uize.com
| /____/ /__/_| | COPYRIGHT : (c)2014-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.Class.mChildObjectBindings= module defines a suite of unit tests for the =Uize.Class.mChildObjectBindings= mixin module.
*DEVELOPERS:* `Ben Ilegbodu`, original code contributed by `Zazzle Inc.`
*/
Uize.module ({
name:'Uize.Test.Uize.Class.mChildObjectBindings',
superclass:'Uize.Test.Class',
required:'Uize.Widget',
builder:function (_superclass) {
'use strict';
var
_Uize = Uize,
_bindingsFunctionName = 'childBindings',
_addMethodName = 'addChild',
_removeMethodName = 'removeChild',
_childObjectsInstancePropertyName = 'children',
_bindingWiringsStaticDataName = 'mChildObjectBindings_' + _bindingsFunctionName + '_wirings',
_class = _superclass.subclass({
moduleToTest:'Uize.Class.mChildObjectBindings'
})
;
function _getRandomPropertyValue() { return Math.floor(Math.random() * 10000) }
function _addChildren(_instance, _children) {
_children = Uize.map (
_children,
function (_childProperties, _childName) {
if (_childName) {
var _objectClass = _Uize.Widget;
if ('objectClass' in _childProperties) {
_childProperties = _Uize.clone(_childProperties);
_objectClass = _childProperties.objectClass;
delete _childProperties.objectClass;
}
_instance[_addMethodName](_childName, _objectClass, _childProperties);
}
}
);
}
function _getTestClass(_bindings, _stateProperties, _children) {
return Uize.Widget.subclass (
_Uize.copyInto(
{
mixins:_Uize.Class.mChildObjectBindings,
childObjectBindings:{
declaration:_bindingsFunctionName,
instanceProperty:_childObjectsInstancePropertyName,
addedInstanceProperty:'addedChildren',
stateProperty:_childObjectsInstancePropertyName
},
omegastructor:function() { _addChildren(this, _children) },
stateProperties:_stateProperties
},
_Uize.pairUp(_bindingsFunctionName, _bindings)
)
);
}
function _getTestClassInstance(_bindings, _stateProperties, _children) {
return _getTestClass(_bindings, _stateProperties, _children)({name:'parent'});
}
function _generateTest(_title, _bindingsVerbose, _bindingsShorthand, _expectedBindings) {
var
_generatedTests = [],
_stateProperties,
_children
;
function _generateSyntaxTests(_isVerbose) {
var _bindings = _isVerbose ? _bindingsVerbose : _bindingsShorthand;
function _getSyntaxTestClass(_omitChildren) {
return _getTestClass(_bindings, _stateProperties, !_omitChildren && _children);
}
function _getSyntaxTestClassInstance(_omitChildren) {
return _getSyntaxTestClass(_omitChildren)({name:'parent'});
}
function _generateTestsForAll(_expectFunc, _omitChildren) {
var _tests = [];
if (!Uize.isEmpty(_expectFunc)) {
Uize.forEach(
_expectedBindings,
function(_bindingsForProperty, _propertyName) {
Uize.forEach(
_bindingsForProperty,
function(_bindingsForChild, _childName) {
Uize.forEach(
_bindingsForChild,
function(_binding) {
_tests.push({
title:'Test for: ' + _propertyName + ' ' + _binding.direction + ' ' + (_childName || '[self]') + '.' + _binding.property,
test:function(_continue) {
_expectFunc.call(
this,
Uize.copyInto(
{
instance:_getSyntaxTestClassInstance(_omitChildren),
instanceProperty:_propertyName,
child:_childName
},
_binding
),
_continue
)
;}
});
}
);
}
);
}
);
}
return _tests;
}
return {
title:(_isVerbose ? 'Verbose' : 'Shorthand') + ' Syntax',
test:[
{
title:'Class is not null',
test:function() { return this.expectNonNull(_getSyntaxTestClass()) }
},
{
title:'Instance without any children is not null (instance works without any children)',
test:function() { return this.expectNonNull(_getSyntaxTestClassInstance(true)) }
},
{
title:'Instance with children is not null (children aren\'t wired before they are added)',
test:function() { return this.expectNonNull(_getSyntaxTestClassInstance()) }
},
{
title:'State property is synched with child\'s state property in the correct direction when child is added',
test:_generateTestsForAll(
function(_binding, _continue) {
var
_instance = _binding.instance,
_initialValue = _instance.get(_binding.instanceProperty),
_aToB = _binding.aToB || Uize.returnX,
_bToA = Uize.returnX
;
// Only when the direction is from child to instance does the child's initial value get synched to the instance
if (_binding.direction == '<-') {
_initialValue = _binding.child
? _children[_binding.child][_binding.property]
: _instance.get(_binding.property)
;
_aToB = Uize.returnX;
_bToA = _binding.bToA || Uize.returnX;
}
_addChildren(_instance, _children);
var
_child = _binding.child
? _instance[_childObjectsInstancePropertyName][_binding.child]
: _instance
;
_continue(
this.expect(_bToA(_initialValue), _instance.get(_binding.instanceProperty))
&& this.expect(_aToB(_initialValue), _child.get(_binding.property))
);
},
true
)
},
{
title:'When instance value changes, child value updates to same value (when applicable)',
test:_generateTestsForAll(
function(_binding, _continue) {
// we're not synching parent -> child so just keep going
if (_binding.direction == '<-') {
_continue(true);
return;
}
var
m = this,
_instance = _binding.instance,
_newValue = _getRandomPropertyValue(),
_aToB = _binding.aToB || Uize.returnX,
_child = _binding.child
? _instance[_childObjectsInstancePropertyName][_binding.child]
: _instance
;
// set instance to new value
_instance.set(_binding.instanceProperty, _newValue);
_continue(m.expect(_aToB(_newValue), _child.get(_binding.property)));
}
)
},
{
title:'When child object value changes, instance value updates to same value (when applicable)',
test:_generateTestsForAll(
function(_binding, _continue) {
// we're not synching child -> parent so just skip
if (_binding.direction == '->') {
_continue(true);
return;
}
var
m = this,
_instance = _binding.instance,
_newValue = _getRandomPropertyValue(),
_bToA = _binding.bToA || Uize.returnX,
_child = _binding.child
? _instance[_childObjectsInstancePropertyName][_binding.child]
: _instance
;
// set instance to new value
_child.set(_binding.property, _newValue);
_continue(m.expect(_bToA(_newValue), _instance.get(_binding.instanceProperty)));
}
)
}
]
};
}
if (_expectedBindings) {
_children = {};
_stateProperties = {};
for (var _property in _expectedBindings) {
var _bindingsForProperty = _expectedBindings[_property];
_stateProperties[_property] = {
name:_property,
value:_getRandomPropertyValue()
};
for (var _childName in _bindingsForProperty) {
var _bindingsForChild = _bindingsForProperty[_childName];
for (var _bindingNo = -1; ++_bindingNo < _bindingsForChild.length;) {
var
_childPropertyName = _bindingsForChild[_bindingNo].property,
_propertyValue = _getRandomPropertyValue()
;
if (_childName)
(_children[_childName] = _children[_childName] || {})[_childPropertyName] = _propertyValue;
else
_stateProperties[_childPropertyName] = {
name:_childPropertyName,
value:_propertyValue
};
}
}
}
}
(!_expectedBindings || _bindingsShorthand)
&& _generatedTests.push(_generateSyntaxTests());
(!_expectedBindings || _bindingsVerbose)
&& _generatedTests.push(_generateSyntaxTests(true));
return {
title:_title,
test:_generatedTests
};
}
return _class.declare({
set:{
test:[
_class.requiredModulesTest(),
{
title:'Empty',
test:[
_generateTest('When no bindings are specified, no state properties are bound'),
_generateTest('When empty bindings are specified, no state properties are bound', {}, {})
]
},
{
title:'Errors',
test:[
_generateTest(
'When no child is specified, nothing is bound (and no errors are thrown)',
{
propertyA:{}
},
{
propertyA:''
}
),
_generateTest(
'When malformed shorthand syntax is specified, nothing is bound (and no errors are thrown)',
undefined,
{
propertyA:'#child',
propertyB:'child.'
}
)
]
},
{
title:'Single State Property Binding',
test:[
{
title:'Single Child Binding',
test:[
_generateTest(
'When only the child property is specified, the child is bound to same-named state property bi-directionally',
{
propertyA:{
child:'childA'
}
},
{
propertyA:'childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'When the child & property properties are specified, the child is bound to the state property bi-directionally',
{
propertyA:{
child:'childA',
property:'childPropertyA'
}
},
{
propertyA:'childA.childPropertyA'
},
{
propertyA:{
childA:[
{
property:'childPropertyA',
direction:'<->'
}
]
}
}
),
{
title:'When the child & direction properties are specified, the child is bound to same-named state property according to the direction',
test:[
_generateTest(
'<->',
{
propertyA:{
child:'childA',
direction:'<->'
}
},
{
propertyA:'<->childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'->',
{
propertyA:{
child:'childA',
direction:'->'
}
},
{
propertyA:'->childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->'
}
]
}
}
),
_generateTest(
'<-',
{
propertyA:{
child:'childA',
direction:'<-'
}
},
{
propertyA:'<-childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-'
}
]
}
}
)
]
},
{
title:'When the child & valueAdapter properties are specified, the child is bound to same-named state property via the appropriate value transformer',
test:[
_generateTest(
'Empty value adapter',
{
propertyA:{
child:'childA',
valueAdapter:{}
}
},
{
propertyA:'childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'String value transformers',
{
propertyA:{
child:'childA',
valueAdapter:{
aToB:'value * 2',
bToA:'value / 2'
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value * 2 },
bToA:function(_value) { return _value / 2 }
}
]
}
}
),
_generateTest(
'Function value transformers',
{
propertyA:{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
]
}
}
),
_generateTest(
'->',
{
propertyA:{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value - 15 }
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value - 15 }
}
]
}
}
),
_generateTest(
'<-',
{
propertyA:{
child:'childA',
direction:'<-',
valueAdapter:{
bToA:'value + 7'
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 7 }
}
]
}
}
)
]
}
]
},
{
title:'Multiple Child Binding',
test:[
_generateTest(
'When only the child property is specified, each child is bound to same-named state property bi-directionally',
{
propertyA:[
{
child:'childA'
},
{
child:'childB'
}
]
},
{
propertyA:[
'childA',
'childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'When the child & property properties are specified, each child is bound to the state property bi-directionally',
{
propertyA:[
{
child:'childA',
property:'childPropertyA'
},
{
child:'childB',
property:'childPropertyA'
}
]
},
{
propertyA:[
'childA.childPropertyA',
'childB.childPropertyA'
]
},
{
propertyA:{
childA:[
{
property:'childPropertyA',
direction:'<->'
}
],
childB:[
{
property:'childPropertyA',
direction:'<->'
}
]
}
}
),
{
title:'When the child & direction properties are specified, each child is bound to same-named state property according to the direction',
test:[
_generateTest(
'<->',
{
propertyA:[
{
child:'childA',
direction:'<->'
},
{
child:'childB',
direction:'<->'
}
]
},
{
propertyA:[
'<->childA',
'<->childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'->',
{
propertyA:[
{
child:'childA',
direction:'->'
},
{
child:'childB',
direction:'->'
}
]
},
{
propertyA:[
'->childA',
'->childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->'
}
],
childB:[
{
property:'propertyA',
direction:'->'
}
]
}
}
)/*,
_generateTest(
'<-',
{
propertyA:[
{
child:'childA',
direction:'<-'
},
{
child:'childB',
direction:'<-'
}
]
},
{
propertyA:[
'<-childA',
'<-childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-'
}
],
childB:[
{
property:'propertyA',
direction:'<-'
}
]
}
}
)*/
]
},
{
title:'When the child & valueAdapter properties are specified, each child is bound to same-named state property via the appropriate value transformer',
test:[
_generateTest(
'Empty value adapter',
{
propertyA:[
{
child:'childA',
valueAdapter:{}
},
{
child:'childB',
valueAdapter:{}
}
]
},
{
propertyA:['childA', 'childB']
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'String value transformers',
{
propertyA:[
{
child:'childA',
valueAdapter:{
aToB:'value * 2',
bToA:'value / 2'
}
},
{
child:'childB',
valueAdapter:{
aToB:'value + 2',
bToA:'value - 2'
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value * 2 },
bToA:function(_value) { return _value / 2 }
}
],
childB:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value + 2 },
bToA:function(_value) { return _value - 2 }
}
]
}
}
),
_generateTest(
'Function value transformers',
{
propertyA:[
{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
},
{
child:'childB',
valueAdapter:{
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
],
childB:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
]
}
}
),
_generateTest(
'->',
{
propertyA:[
{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value - 15 }
}
},
{
child:'childB',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value * 15 }
}
},
{
child:'childC',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value / 15 }
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value - 15 }
}
],
childB:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value * 15 }
}
],
childC:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value / 15 }
}
]
}
}
)/*,
_generateTest(
'<-',
{
propertyA:[
{
child:'childA',
direction:'<-',
valueAdapter:{
bToA:'value + 7'
}
},
{
child:'childB',
direction:'<-',
valueAdapter:{
bToA:'value + 17'
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 7 }
}
],
childB:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 17 }
}
]
}
}
)*/
]
}
]
}
]
},
{
title:'Multiple State Property Binding',
test:[
{
title:'Single Child Binding',
test:[
_generateTest(
'When only the child property is specified, the child is bound to same-named state property bi-directionally',
{
propertyA:{
child:'childA'
},
propertyB:{
child:'childA'
}
},
{
propertyA:'childA',
propertyB:'childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'When the child & property properties are specified, the child is bound to the state property bi-directionally',
{
propertyA:{
child:'childA',
property:'childPropertyA'
},
propertyB:{
child:'childA',
property:'childPropertyB'
}
},
{
propertyA:'childA.childPropertyA',
propertyB:'childA.childPropertyB'
},
{
propertyA:{
childA:[
{
property:'childPropertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'childPropertyB',
direction:'<->'
}
]
}
}
),
{
title:'When the child & direction properties are specified, the child is bound to same-named state property according to the direction',
test:[
_generateTest(
'<->',
{
propertyA:{
child:'childA',
direction:'<->'
},
propertyB:{
child:'childA',
direction:'<->'
}
},
{
propertyA:'<->childA',
propertyB:'<->childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'->',
{
propertyA:{
child:'childA',
direction:'->'
},
propertyB:{
child:'childA',
direction:'->'
}
},
{
propertyA:'->childA',
propertyB:'->childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'->'
}
]
}
}
),
_generateTest(
'<-',
{
propertyA:{
child:'childA',
direction:'<-'
},
propertyB:{
child:'childA',
direction:'<-'
}
},
{
propertyA:'<-childA',
propertyB:'<-childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<-'
}
]
}
}
)
]
},
{
title:'When the child & valueAdapter properties are specified, the child is bound to same-named state property via the appropriate value transformer',
test:[
_generateTest(
'Empty value adapter',
{
propertyA:{
child:'childA',
valueAdapter:{}
},
propertyB:{
child:'childA',
valueAdapter:{}
}
},
{
propertyA:'childA',
propertyB:'childA'
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'String value transformers',
{
propertyA:{
child:'childA',
valueAdapter:{
aToB:'value * 2',
bToA:'value / 2'
}
},
propertyB:{
child:'childA',
valueAdapter:{
aToB:'value + 2',
bToA:'value - 2'
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value * 2 },
bToA:function(_value) { return _value / 2 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value + 2 },
bToA:function(_value) { return _value - 2 }
}
]
}
}
),
_generateTest(
'Function value transformers',
{
propertyA:{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
},
propertyB:{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
]
}
}
),
_generateTest(
'->',
{
propertyA:{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value - 15 }
}
},
propertyB:{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value * 15 }
}
},
propertyC:{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value / 15 }
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value - 15 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'->',
aToB:function(_value) { return _value * 15 }
}
]
},
propertyC:{
childA:[
{
property:'propertyC',
direction:'->',
aToB:function(_value) { return _value / 15 }
}
]
}
}
),
_generateTest(
'<-',
{
propertyA:{
child:'childA',
direction:'<-',
valueAdapter:{
bToA:'value + 7'
}
},
propertyB:{
child:'childA',
direction:'<-',
valueAdapter:{
bToA:'value + 17'
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 7 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<-',
bToA:function(_value) { return _value + 17 }
}
]
}
}
)
]
}
]
},
{
title:'Multiple Child Binding',
test:[
_generateTest(
'When only the child property is specified, each child is bound to same-named state property bi-directionally',
{
propertyA:[
{
child:'childA'
},
{
child:'childB'
}
],
propertyB:[
{
child:'childA'
},
{
child:'childB'
}
]
},
{
propertyA:[
'childA',
'childB'
],
propertyB:[
'childA',
'childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
],
childB:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'When the child & property properties specified, each child is bound to the state property bi-directionally',
{
propertyA:[
{
child:'childA',
property:'childPropertyA'
},
{
child:'childB',
property:'childPropertyA'
}
],
propertyB:[
{
child:'childA',
property:'childPropertyB'
},
{
child:'childB',
property:'childPropertyB'
},
{
child:'childC',
property:'childPropertyB'
}
]
},
{
propertyA:[
'childA.childPropertyA',
'childB.childPropertyA'
],
propertyB:[
'childA.childPropertyB',
'childB.childPropertyB',
'childC.childPropertyB'
]
},
{
propertyA:{
childA:[
{
property:'childPropertyA',
direction:'<->'
}
],
childB:[
{
property:'childPropertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'childPropertyB',
direction:'<->'
}
],
childB:[
{
property:'childPropertyB',
direction:'<->'
}
],
childC:[
{
property:'childPropertyB',
direction:'<->'
}
]
}
}
),
{
title:'When the child & direction properties are specified, each child is bound to same-named state property according to the direction',
test:[
_generateTest(
'<->',
{
propertyA:[
{
child:'childA',
direction:'<->'
},
{
child:'childB',
direction:'<->'
},
{
child:'childC',
direction:'<->'
},
{
child:'childD',
direction:'<->'
}
],
propertyB:[
{
child:'childA',
direction:'<->'
},
{
child:'childB',
direction:'<->'
}
]
},
{
propertyA:[
'<->childA',
'<->childB',
'<->childC',
'<->childD'
],
propertyB:[
'<->childA',
'<->childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
],
childC:[
{
property:'propertyA',
direction:'<->'
}
],
childD:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
],
childB:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'->',
{
propertyA:[
{
child:'childA',
direction:'->'
},
{
child:'childB',
direction:'->'
},
{
child:'childC',
direction:'->'
}
],
propertyB:[
{
child:'childA',
direction:'->'
},
{
child:'childB',
direction:'->'
},
{
child:'childC',
direction:'->'
}
],
propertyC:[
{
child:'childA',
direction:'->'
},
{
child:'childB',
direction:'->'
}
]
},
{
propertyA:[
'->childA',
'->childB',
'->childC'
],
propertyB:[
'->childA',
'->childB',
'->childC'
],
propertyC:[
'->childA',
'->childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->'
}
],
childB:[
{
property:'propertyA',
direction:'->'
}
],
childC:[
{
property:'propertyA',
direction:'->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'->'
}
],
childB:[
{
property:'propertyB',
direction:'->'
}
],
childC:[
{
property:'propertyB',
direction:'->'
}
]
},
propertyC:{
childA:[
{
property:'propertyC',
direction:'->'
}
],
childB:[
{
property:'propertyC',
direction:'->'
}
]
}
}
)/*,
_generateTest(
'<-',
{
propertyA:[
{
child:'childA',
direction:'<-'
},
{
child:'childB',
direction:'<-'
},
{
child:'childC',
direction:'<-'
},
{
child:'childD',
direction:'<-'
}
],
propertyB:[
{
child:'childA',
direction:'<-'
},
{
child:'childB',
direction:'<-'
}
]
},
{
propertyA:[
'<-childA',
'<-childB',
'<-childC',
'<-childD'
],
propertyB:[
'<-childA',
'<-childB'
]
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-'
}
],
childB:[
{
property:'propertyA',
direction:'<-'
}
],
childC:[
{
property:'propertyA',
direction:'<-'
}
],
childD:[
{
property:'propertyA',
direction:'<-'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<-'
}
],
childB:[
{
property:'propertyB',
direction:'<-'
}
]
}
}
)*/
]
},
{
title:'When the child & valueAdapter properties are specified, each child is bound to same-named state property via the appropriate value transformer',
test:[
_generateTest(
'Empty value adapter',
{
propertyA:[
{
child:'childA',
valueAdapter:{}
},
{
child:'childB',
valueAdapter:{}
}
],
propertyB:[
{
child:'childA',
valueAdapter:{}
},
{
child:'childB',
valueAdapter:{}
}
]
},
{
propertyA:['childA', 'childB'],
propertyB:['childA', 'childB']
},
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->'
}
],
childB:[
{
property:'propertyA',
direction:'<->'
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->'
}
],
childB:[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'String value transformers',
{
propertyA:[
{
child:'childA',
valueAdapter:{
aToB:'value * 2',
bToA:'value / 2'
}
},
{
child:'childB',
valueAdapter:{
aToB:'value + 2',
bToA:'value - 2'
}
}
],
propertyB:[
{
child:'childA',
valueAdapter:{
aToB:'value - 20',
bToA:'value + 20'
}
},
{
child:'childB',
valueAdapter:{
aToB:'value + 20',
bToA:'value - 20'
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value * 2 },
bToA:function(_value) { return _value / 2 }
}
],
childB:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value + 2 },
bToA:function(_value) { return _value - 2 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value - 20 },
bToA:function(_value) { return _value + 20 }
}
],
childB:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value + 20 },
bToA:function(_value) { return _value - 20 }
}
]
}
}
),
_generateTest(
'Function value transformers',
{
propertyA:[
{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
},
{
child:'childB',
valueAdapter:{
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
}
],
propertyB:[
{
child:'childA',
valueAdapter:{
aToB:function(_value) { return _value / 10 },
bToA:function(_value) { return _value * 10 }
}
},
{
child:'childB',
valueAdapter:{
aToB:function(_value) { return _value - 10 },
bToA:function(_value) { return _value + 10 }
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value / 2 },
bToA:function(_value) { return _value * 2 }
}
],
childB:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value - 2 },
bToA:function(_value) { return _value + 2 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value / 10 },
bToA:function(_value) { return _value * 10 }
}
],
childB:[
{
property:'propertyB',
direction:'<->',
aToB:function(_value) { return _value - 10 },
bToA:function(_value) { return _value + 10 }
}
]
}
}
),
_generateTest(
'->',
{
propertyA:[
{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value - 15 }
}
},
{
child:'childB',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value * 15 }
}
},
{
child:'childC',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value / 15 }
}
}
],
propertyB:[
{
child:'childA',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value - 15 }
}
},
{
child:'childB',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value * 15 }
}
},
{
child:'childC',
direction:'->',
valueAdapter:{
aToB:function(_value) { return _value / 15 }
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value - 15 }
}
],
childB:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value * 15 }
}
],
childC:[
{
property:'propertyA',
direction:'->',
aToB:function(_value) { return _value / 15 }
}
]
},
propertyB:{
childA:[
{
property:'propertyB',
direction:'->',
aToB:function(_value) { return _value - 15 }
}
],
childB:[
{
property:'propertyB',
direction:'->',
aToB:function(_value) { return _value * 15 }
}
],
childC:[
{
property:'propertyB',
direction:'->',
aToB:function(_value) { return _value / 15 }
}
]
}
}
)/*,
_generateTest(
'<-',
{
propertyA:[
{
child:'childA',
direction:'<-',
valueAdapter:{
bToA:'value + 7'
}
},
{
child:'childB',
direction:'<-',
valueAdapter:{
bToA:'value + 17'
}
}
]
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 7 }
}
],
childB:[
{
property:'propertyA',
direction:'<-',
bToA:function(_value) { return _value + 17 }
}
]
}
}
)*/
]
}
]
}
]
},
{
title:'Conditional binding (when)',
test:[
{
title:'When *when* condition is specified and it isn\'t initially met, the children do *not* initially have the instance\'s properties',
test:_Uize.map(
[
{
child:'childA',
when:'whenProperty'
},
'childA:whenProperty'
],
function(_propertyBinding) {
return {
title:_Uize.isPlainObject(_propertyBinding) ? 'Verbose Syntax' : 'Shorthand Syntax',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{propertyA:_propertyBinding},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_childInstancePropertyValue}}
)
;
return m.expect(_childInstancePropertyValue, _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
};
}
)
},
{
title:'When *when* condition is specified and it isn\'t initially met, the instance\'s properties do not initially have the child\'s',
test:_Uize.map(
[
{
child:'childA',
property:'propertyA',
direction:'<-',
when:'whenProperty'
},
'<-childA.propertyA:whenProperty'
],
function(_propertyBinding) {
return {
title:_Uize.isPlainObject(_propertyBinding) ? 'Verbose Syntax' : 'Shorthand Syntax',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{propertyA:_propertyBinding},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
return m.expect(_instancePropertyValue, _instance.get('propertyA'));
}
};
}
)
},
{
title:'When *when* condition is specified and it isn\'t initially met, but becomes met, the children\'s properties are synced',
test:_Uize.map(
[
{
child:'childA',
when:'whenProperty'
},
'childA:whenProperty'
],
function(_propertyBinding) {
return {
title:_Uize.isPlainObject(_propertyBinding) ? 'Verbose Syntax' : 'Shorthand Syntax',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{propertyA:_propertyBinding},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
_instance.met('whenProperty');
return m.expect(_instancePropertyValue, _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
};
}
)
},
{
title:'When *when* condition is specified and it isn\'t initially met, but becomes met, the intance\'s properties are synced',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
direction:'<-',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_childInstancePropertyValue}}
)
;
_instance.met('whenProperty');
return m.expect(_childInstancePropertyValue, _instance.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it isn\'t initially met and the instance\'s value changes, the children still do *not* have the instance\'s properties',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_childInstancePropertyValue}}
)
;
_instance.set('propertyA', _getRandomPropertyValue());
return m.expect(_childInstancePropertyValue, _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it isn\'t initially met and the child\'s value changes, the instance\'s properties still do *not* have the child\'s',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
direction:'<-',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_getRandomPropertyValue()}}
),
_child = _instance[_childObjectsInstancePropertyName].childA
;
_child.set('propertyA', _getRandomPropertyValue());
return m.expect(_instancePropertyValue, _instance.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it is initially met, the children\'s properties are synced',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:true}
},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
return m.expect(_instancePropertyValue, _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it isn initially met, the intance\'s properties are synced',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
direction:'<-',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:true}
},
{childA:{propertyA:_childInstancePropertyValue}}
)
;
return m.expect(_childInstancePropertyValue, _instance.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it is initially met, but becomes unmet and the instance\'s value changes, the children still do *not* have the instance\'s properties',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:true}
},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
_instance.unmet('whenProperty');
_instance.set('propertyA', _getRandomPropertyValue());
return m.expect(_instancePropertyValue, _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it is initially met, but becomes unmet and the child\'s value changes, the instance\'s properties still do *not* have the child\'s',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
direction:'<-',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:true}
},
{childA:{propertyA:_childInstancePropertyValue}}
),
_child = _instance[_childObjectsInstancePropertyName].childA
;
_instance.unmet('whenProperty');
_child.set('propertyA', _getRandomPropertyValue());
return m.expect(_childInstancePropertyValue, _instance.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it isn\'t initially met, the child is removed, and condition becomes met, the child\'s value is *not* synched',
test:function() {
var
m = this,
_childInstancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_childInstancePropertyValue}}
),
_child = _instance[_childObjectsInstancePropertyName].childA
;
_instance.removeChild(_child);
_instance.met('whenProperty');
return m.expect(_childInstancePropertyValue, _child.get('propertyA'));
}
},
{
title:'When *when* condition is specified and it isn\'t initially met, the child is removed, and condition becomes met, the instance\'s properties still do *not* have the child\'s',
test:function() {
var
m = this,
_instancePropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{
propertyA:{
child:'childA',
direction:'<-',
when:'whenProperty'
}
},
{
propertyA:{name:'propertyA',value:_instancePropertyValue},
propertyB:{name:'whenProperty', value:false}
},
{childA:{propertyA:_getRandomPropertyValue()}}
),
_child = _instance[_childObjectsInstancePropertyName].childA
;
_instance.removeChild(_child);
_instance.met('whenProperty');
return m.expect(_instancePropertyValue, _instance.get('propertyA'));
}
}
]
},
{
title:'Edge Cases',
test:[
{
title:'Child is self (parent) widget',
test:[
_generateTest(
'Omit child name (and no child property specified so it\'s pointing back to itself)',
{
propertyA:{}
},
{
propertyA:''
},
{
propertyA:{
'':[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'Omit child name (same property name specified so it\'s pointing back to itself)',
{
propertyA:{
property:'propertyA'
}
},
{
propertyA:'.propertyA'
},
{
propertyA:{
'':[
{
property:'propertyA',
direction:'<->'
}
]
}
}
),
_generateTest(
'Omit child name (different property name)',
{
propertyA:{
property:'propertyB'
}
},
{
propertyA:'.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'Specify child name (null)',
{
propertyA:{
child:null,
property:'propertyB'
}
},
{
propertyA:'.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'Specify child name (undefined)',
{
propertyA:{
child:undefined,
property:'propertyB'
}
},
{
propertyA:'.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'Specify child name (empty)',
{
propertyA:{
child:'',
property:'propertyB'
}
},
{
propertyA:'.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'Specify child name, property & direction (<->)',
{
propertyA:{
child:'',
property:'propertyB',
direction:'<->'
}
},
{
propertyA:'<->.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<->'
}
]
}
}
),
_generateTest(
'Specify child name, property & direction (->)',
{
propertyA:{
child:'',
property:'propertyB',
direction:'->'
}
},
{
propertyA:'->.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'->'
}
]
}
}
),
_generateTest(
'Specify child name, property & direction (<-)',
{
propertyA:{
child:'',
property:'propertyB',
direction:'<-'
}
},
{
propertyA:'<-.propertyB'
},
{
propertyA:{
'':[
{
property:'propertyB',
direction:'<-'
}
]
}
}
)
]
},
{
title:'When a child is added, it is created with its parent\'s initial state for its bound properties to the parent',
test:function() {
var
m = this,
_childChangeHandlerCallCount = 0,
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_getRandomPropertyValue()}}
),
_childClass = _Uize.Widget.subclass({
stateProperties:{
propertyA:{
name:'propertyA',
onChange:function() {
_childChangeHandlerCallCount++;
},
value:_getRandomPropertyValue()
}
}
})
;
_instance[_addMethodName]('childA', _childClass);
return m.expect(1, _childChangeHandlerCallCount);
}
},
{
title:'When a child is bound bi-directionally and the instance has data and the child doesn\'t, the instance should be the driver object',
test:function() {
var
_instanceInitialPropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_instanceInitialPropertyValue}},
{childA:{}}
)
;
return this.expect(_instanceInitialPropertyValue, _instance.get('propertyA'))
&& this.expect(_instance.get('propertyA'), _instance[_childObjectsInstancePropertyName].childA.get('propertyA'))
;
}
},
{
title:'When a child is bound bi-directionally and it has data and the instance doesn\'t, the child should be the driver object',
test:function() {
var
_childObjectInitialPropertyValue = _getRandomPropertyValue(),
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA'}},
{childA:{propertyA:_childObjectInitialPropertyValue}}
),
_childAObject = _instance[_childObjectsInstancePropertyName].childA
;
return this.expect(_childObjectInitialPropertyValue, _childAObject.get('propertyA'))
&& this.expect(_childAObject.get('propertyA'), _instance.get('propertyA'))
;
}
},
/*_generateTest(
'Thrashing doesn\'t occurr with bi-directional bindings (best demonstrated with mis-match value adapters)',
{
propertyA:{
child:'childA',
valueAdapter:{
aToB:'value * 3',
bToA:'value / 2'
}
}
},
null,
{
propertyA:{
childA:[
{
property:'propertyA',
direction:'<->',
aToB:function(_value) { return _value * 3},
bToA:function(_value) { return _value / 2 }
}
]
}
}
),*/
{
title:'When a bound child is removed, a state property change in parent after removal is properly handled (no errors and not fired on child)',
test:function() {
var
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_getRandomPropertyValue()}},
{childA:{propertyA:_getRandomPropertyValue()}}
),
_childToRemove = _instance[_childObjectsInstancePropertyName].childA // keep reference so we'll have it after removal
;
_instance[_removeMethodName](_childToRemove);
// set instance to new value to see if child will also update (which it shouldn't)
_instance.set('propertyA', _getRandomPropertyValue());
return this.expect(true, _instance.get('propertyA') != _childToRemove.get('propertyA'));
}
},
{
title:'When a bound child is removed, a state property change in child after removal is properly handled (no errors and not fired on parent)',
test:function() {
var
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_getRandomPropertyValue()}},
{childA:{propertyA:_getRandomPropertyValue()}}
),
_childToRemove = _instance[_childObjectsInstancePropertyName].childA // keep reference so we'll have it after removal
;
_instance[_removeMethodName](_childToRemove);
// set instance to new value to see if child will also update (which it shouldn't)
_childToRemove.set('propertyA', _getRandomPropertyValue());
return this.expect(true, _instance.get('propertyA') != _childToRemove.get('propertyA'));
}
},
{
title:'When a bound child is removed, and a new same-named child is re-added, the new child\'s state is sync\'d',
test:function() {
var
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_getRandomPropertyValue()}},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
// first remove the child
_instance[_removeMethodName]('childA');
// then add back a new child with the same name (which should get bound again)
_instance.addChild('childA', Uize.Widget, {propertyA:_getRandomPropertyValue()});
// state should match when the child's state is synched
return this.expect(_instance.get('propertyA'), _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
},
{
title:'When a bound child is removed, and a new same-named child is re-added, a change in instance state should be reflected in child\'s',
test:function() {
var
_instance = _getTestClassInstance(
{propertyA:'childA'},
{propertyA:{name:'propertyA',value:_getRandomPropertyValue()}},
{childA:{propertyA:_getRandomPropertyValue()}}
)
;
// first remove the child
_instance[_removeMethodName]('childA');
// then add back a new child with the same name (which should get bound again)
_instance.addChild('childA', Uize.Widget, {propertyA:_getRandomPropertyValue()});
// set instance to new value (which should get bound to child)
_instance.set('propertyA', _getRandomPropertyValue());
return this.expect(_instance.get('propertyA'), _instance[_childObjectsInstancePropertyName].childA.get('propertyA'));
}
},
{
title:'When a subclass declares the same state property/child/child state property combination, the base class\' declaration is overridden',
test:function() {
var
_Class = _getTestClass(
{
propertyA:[
{
child:'childA',
property:'childPropertyA',
direction:'<->',
valueAdapter:{
aToB:'value * 2',
bToA:'value / 2'
}
},
'childB'
]
},
{
propertyA:{name:'propertyA',value:_getRandomPropertyValue()},
propertyB:{name:'propertyB',value:_getRandomPropertyValue()}
},
{
childA:{
propertyA:_getRandomPropertyValue(),
propertyB:_getRandomPropertyValue()
},
childB:{
propertyA:_getRandomPropertyValue()
}
}
),
_Subclass = _Class.subclass(
_Uize.pairUp(
_bindingsFunctionName,
{
propertyA:'->childA.childPropertyA'
}
)
),
_ClassBindingsData = _Class[_bindingWiringsStaticDataName],
_SubclassBindingsData = _Subclass[_bindingWiringsStaticDataName]
;
return this.expectSameAs(_SubclassBindingsData.childB['propertyA/propertyA'], _ClassBindingsData.childB['propertyA/propertyA'])
&& this.expectNotSameAs(_SubclassBindingsData.childA['propertyA/childPropertyA'], _ClassBindingsData.childA['propertyA/childPropertyA'])
&& this.expect(1, Uize.keys(_SubclassBindingsData.childA).length)
&& this.expectNonNull(1, _SubclassBindingsData.childA['propertyA/childPropertyA'])
&& this.expect(1, Uize.keys(_SubclassBindingsData.childB).length)
&& this.expectNonNull(1, _SubclassBindingsData.childB['propertyA/propertyA'])
;
}
}
]
}
]
}
});
}
});