/*______________
| ______ | 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.mEventBindings Class
| / / / |
| / / / /| | ONLINE : http://uize.com
| /____/ /__/_| | COPYRIGHT : (c)2014 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.mEventBindings= module defines a suite of unit tests for the =Uize.Widget.mEventBindings= mixin module.
*DEVELOPERS:* `Ben Ilegbodu`, original code contributed by `Zazzle Inc.`
*/
Uize.module ({
name:'Uize.Test.Uize.Widget.mEventBindings',
required:[
'Uize.Comm',
'Uize.Class'
],
builder:function () {
'use strict';
var
_global = Uize.global(),
_processArrayAsync = Uize.Comm.processArrayAsync,
_defaultHandler = function(_event, _source) { _event.handler.call(this, _event, _source) },
_defaultChangedHandler = function(_event, _source) { this.get(_event.name.slice(8)).call(this, _event, _source) },
_originalWindow
;
function _getMockDomNode(_name) {
return Uize.Class.subclass({
alphastructor:function() {
this._events = {};
},
instanceProperties:{
tagName:'DIV',
nodeType:1
},
instanceMethods:{
addEventListener:function(_eventName, _handler) {
(this._events[_eventName] || (this._events[_eventName] = [])).push(_handler);
},
triggerEvent:function(_event) {
this._events[_event.name]
&& Uize.applyAll(this, this._events[_event.name], [_event]);
}
},
stateProperties:{
_name:'name'
}
}) ({name:_name});
}
function _getTestWidgetClass(_eventBindings, _children) {
return Uize.Widget.subclass ({
mixins:Uize.Widget.mEventBindings,
omegastructor:function() {
this.addChildren(Uize.lookup(_children, {widgetClass:Uize.Widget}));
},
eventBindings:_eventBindings
});
}
function _getTestWidgetClassInstance(_eventBindings, _children, _nodes, _instanceProperties) {
return _getTestWidgetClass(_eventBindings, _children)(
Uize.copyInto(
{
nodeMap:Uize.map(
Uize.lookup(_nodes),
function(_value, _node) { return _getMockDomNode(_node) }
)
},
_instanceProperties
)
);
}
function _generateTest(_title, _eventBindingsShorthand, _eventBindingsVerbose, _wiredEvents, _deferredChildren) {
function _generateSyntaxTests(_isVerbose) {
var _eventBindings = _isVerbose ? _eventBindingsVerbose : _eventBindingsShorthand;
function _getSyntaxTestWidgetClass(_children) {
return _getTestWidgetClass(_eventBindings, _children);
}
function _getSyntaxTestWidgetClassInstance(_children, _nodes, _instanceProperties) {
return _getTestWidgetClassInstance(_eventBindings, _children, _nodes, _instanceProperties);
}
function _generateFireTests(_type) {
var _fireEventMethodName = _type == 'node' ? 'triggerEvent' : 'fire';
function _generateFireTest(_expectFunc, _instanceProperties) {
return function(_continue) {
if (!_wiredEvents || Uize.isEmpty(_wiredEvents[_type]))
return true;
var
_typeEvents = _wiredEvents[_type],
_names = _type == 'self' ? ['self'] : Uize.keys(_typeEvents),
_testWidgetClassInstance = _getSyntaxTestWidgetClassInstance(
_type == 'child' ? _names : undefined,
_type == 'node' ? _names : undefined,
_instanceProperties
),
_fail = function() { _continue(true) }
;
_processArrayAsync(
_names,
function(_objectName, _nextObjectFunc) {
_processArrayAsync(
(_type == 'self' ? _wiredEvents : _typeEvents)[_objectName],
function(_eventName, _nextEventFunc) {
var
_objectToFireOn = _type == 'self'
? _testWidgetClassInstance
: (_type == 'child'
? _testWidgetClassInstance.children[_objectName]
: _testWidgetClassInstance.getNode(_objectName)
),
_fireEvent = function(_fireHandler) {
_type == 'self' && !_eventName.indexOf('Changed.')
? _objectToFireOn.set(
_eventName.slice(8),
_fireHandler
)
: _objectToFireOn[_fireEventMethodName]({
name:_eventName,
handler:_fireHandler
})
;
},
_verifyEventFired = function(_nextFunc) {
var _handlerNotCalledTimeout = setTimeout(function() { _continue(false) }, 0); // timeout for if handler isn't called
_fireEvent(
function(_event, _source) {
clearTimeout(_handlerNotCalledTimeout);
_expectFunc.call(
this,
{
_nextFunc:_nextFunc,
_testWidgetClassInstance:_testWidgetClassInstance,
_eventName:_eventName,
_event:_event,
_source:_source,
_fail:_fail,
_firedObject:_objectToFireOn
}
);
}
);
},
_verifyEventNotFired = function(_nextFunc) {
var
_handlerNotCalledTimeout = setTimeout(_nextFunc, 0)// timeout for when handler isn't called
;
// Verify event is not fired
_fireEvent(
function() { // handler shouldn't get called
clearTimeout(_handlerNotCalledTimeout);
_continue(false);
}
);
},
_deferredChildrenForEventObject = !Uize.isEmpty(_deferredChildren)
&& (_type == 'self'
? _deferredChildren.self
: (_deferredChildren[_type] && _deferredChildren[_type][_objectName])
),
_deferredChildrenForEvent = _deferredChildrenForEventObject && _deferredChildrenForEventObject[_eventName]
;
_type == 'node' && _testWidgetClassInstance.met('wired');
if (Uize.isEmpty(_deferredChildrenForEvent)) {
_verifyEventFired(_nextEventFunc);
}
else {
_processArrayAsync(
_deferredChildrenForEvent,
function(_deferredChildName, _nextDeferredChildFunc) {
_verifyEventNotFired(
function() {
// Add deferred child
_testWidgetClassInstance.addChild(_deferredChildName, Uize.Widget);
_nextDeferredChildFunc();
}
);
},
function() {
// Verify event is fired
_verifyEventFired(
function() {
// Remove a random child
_testWidgetClassInstance.removeChild(
_deferredChildrenForEvent[
Math.floor(Math.random() * _deferredChildrenForEvent.length)
]
);
// Verify event is not fired & then move onto next event
_verifyEventNotFired(_nextEventFunc);
}
);
}
);
}
},
_nextObjectFunc
);
},
function() { _continue(true) }
);
};
}
return {
title:Uize.capFirstChar(_type) + ' events are successfully fired',
test:[
{
title:'Handler is called',
test:_generateFireTest(
function(_data) { _data._nextFunc() }
)
},
{
title:'"this" context is widget',
test:_generateFireTest(
function(_data) {
this == _data._testWidgetClassInstance
? _data._nextFunc()
: _data._fail()
;
}
)
},
{
title:'First argument is event object',
test:_generateFireTest(
function(_data) {
_data._event.name == _data._eventName
? _data._nextFunc()
: _data._fail()
;
}
)
},
{
title:'Second argument is source object',
test:_generateFireTest(
function(_data) {
_data._source == _data._firedObject
? _data._nextFunc()
: _data._fail()
;
}
)
}
]
};
}
return {
title:(_isVerbose ? 'Verbose' : 'Shorthand') + ' Syntax',
test:[
{
title:'Widget class is a function (not null)',
test:function() { return this.expectFunction(_getSyntaxTestWidgetClass()) }
},
{
title:'Widget instance is an object (not null)',
test:function() { return this.expectObject(_getSyntaxTestWidgetClassInstance()) }
},
_generateFireTests('self'),
_generateFireTests('child'),
{
title:'Node events do not fire before widget is wired',
test:function(_continue) {
if (!_wiredEvents || Uize.isEmpty(_wiredEvents.node))
return true;
var
_nodeNames = Uize.keys(_wiredEvents.node),
_testWidgetClassInstance = _getSyntaxTestWidgetClassInstance(null, _nodeNames)
;
_processArrayAsync(
_nodeNames,
function(_nodeName, _nextNodeFunc) {
_processArrayAsync(
_wiredEvents.node[_nodeName],
function(_eventName, _nextEventFunc) {
// The handler shouldn't be called which means we won't continue to the next node,
// so, set up a timeout to continue on if the handler is not called.
var _notFiredTimeout = setTimeout(_nextEventFunc, 0);
_testWidgetClassInstance.getNode(_nodeName).triggerEvent({
name:_eventName,
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(false); // we should never get here
}
});
},
_nextNodeFunc
);
},
function() { _continue(true) }
);
}
},
_generateFireTests('node')
]
};
}
return {
title:_title,
test:[
_generateSyntaxTests(),
_generateSyntaxTests(true)
]
};
}
return Uize.Test.resolve ({
title:'Uize.Widget.mEventBindings Module Test',
test:[
{
title:'Set the global window object',
test:function() {
_originalWindow = _global.window;
_global.window = _global; // For Uize.Dom.Basics
return true;
}
},
Uize.Test.requiredModulesTest ([
'Uize.Widget',
'Uize.Widget.mEventBindings'
]),
{
title:'Empty bindings',
test:[
_generateTest('When no declarative event bindings are specified, no events are wired'),
_generateTest('When empty event bindings is specified, no events are wired')
]
},
{
title:'Child Events',
test:[
_generateTest(
'When a single child with a single event binding is declared, only that child event is bound',
{
'foo:Click':_defaultHandler
},
{
foo:{
Click:_defaultHandler
}
},
{
child:{
foo:['Click']
}
}
),
_generateTest(
'When a single child with multiple event bindings are declared, only the events for that child are bound',
Uize.lookup(
[
'foo:Click',
'foo:Changed.value',
'foo:Changed.bar'
],
_defaultHandler
),
{
foo:{
Click:_defaultHandler,
'Changed.value':_defaultHandler,
'Changed.bar':_defaultHandler
}
},
{
child:{
foo:['Click', 'Changed.value', 'Changed.bar']
}
}
),
_generateTest(
'When a multiple children each with a single event binding are declared, only the one event for each child is bound',
Uize.lookup(
[
'foo:Changed.bar',
'lorem:Changed.ipsum'
],
_defaultHandler
),
{
foo:{
'Changed.bar':_defaultHandler
},
lorem:{
'Changed.ipsum':_defaultHandler
}
},
{
child:{
foo:['Changed.bar'],
lorem:['Changed.ipsum']
}
}
),
_generateTest(
'When a multiple children each with multiple event bindings are declared, only the events for those children are bound',
Uize.lookup(
[
'foo:Click',
'lorem:Changed.ipsum',
'foo:Changed.value',
'lorem:Changed.dolor',
'foo:Changed.bar',
'a:Changed.b',
'a:c',
'a:d',
'a:Changed.e'
],
_defaultHandler
),
{
foo:{
Click:_defaultHandler,
'Changed.value':_defaultHandler,
'Changed.bar':_defaultHandler
},
lorem:{
'Changed.ipsum':_defaultHandler,
'Changed.dolor':_defaultHandler
},
a:{
'Changed.b':_defaultHandler,
'c':_defaultHandler,
'd':_defaultHandler,
'Changed.e':_defaultHandler
}
},
{
child:{
foo:['Click', 'Changed.value', 'Changed.bar'],
lorem:['Changed.ipsum', 'Changed.dolor'],
a:['Changed.b', 'c', 'd', 'Changed.e']
}
}
),
{
title:'Required Children',
test:[
_generateTest(
'When required is left unspecified for a child event binding, the event is fired normally (w/ no errors)',
{
'foo:Click':{
handler:_defaultHandler
}
},
{
foo:{
Click:{
handler:_defaultHandler
}
}
},
{
child:{
foo:['Click']
}
}
),
_generateTest(
'When a single required child for a child event binding is declared, the event is not fired until the child is added',
{
'foo:Click':{
handler:_defaultHandler,
required:['baz']
}
},
{
foo:{
Click:{
handler:_defaultHandler,
required:['baz']
}
}
},
{
child:{
foo:['Click']
}
},
{
child:{
foo:{
Click:['baz']
}
}
}
),
_generateTest(
'When multiple required children for a child event binding are declared, the event is not fired until all the children are added (and order doesn\'t matter)',
{
'foo:Click':{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
},
{
foo:{
Click:{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
}
},
{
child:{
foo:['Click']
}
},
{
child:{
foo:{
Click:['bat', 'baf', 'baz']
}
}
}
)
]
},
{
title:'Conditional firing (fireIf)',
test:Uize.push(
Uize.map(
[
{
title:'When fireIf is undefined and widget is disabled, child event is fired',
state:{enabled:false}
},
{
title:'When fireIf is undefined and widget is busy, child event is fired',
state:{busy:true}
},
{
title:'When fireIf evaluates to true, child event is fired',
fireIf:'foo',
state:{foo:true}
}
],
function(_testInfo) {
return {
title:_testInfo.title,
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
'foo:Click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(true);
},
fireIf:_testInfo.fireIf
}
},
['foo'],
null,
_testInfo.state
),
_notFiredTimeout
;
_notFiredTimeout = setTimeout(
function() { _continue(false) },
0
);
_testWidgetClassInstance.children.foo.fire('Click');
}
};
}
),
[
{
title:'When fireIf evaluates to false, child event isn\'t fired',
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
'foo:Click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(false);
},
fireIf:'foo'
}
},
['foo'],
null
),
_notFiredTimeout
;
// fire child widget event (shouldn't actually be handled by widget)
_testWidgetClassInstance.children.foo.fire('Click');
_notFiredTimeout = setTimeout(
function() { _continue(true) },
0
);
}
}
]
)
}
]
},
{
title:'Self Events',
test:[
_generateTest(
'When a single event binding is declared, only that event is bound',
Uize.lookup(
[
':Click'
],
_defaultHandler
),
{
'':{
Click:_defaultHandler
}
},
{
self:['Click']
}
),
_generateTest(
'When multiple events binding are declared, only those events are bound',
{
':Click':_defaultHandler,
':Changed.value':_defaultChangedHandler
},
{
'':{
Click:_defaultHandler,
'Changed.value':_defaultChangedHandler
}
},
{
self:['Click', 'Changed.value']
}
),
_generateTest(
'When a single required child for a self event binding is declared, the event is not fired until the child is added',
{
':Click':{
handler:_defaultHandler,
required:['baz']
}
},
{
'':{
Click:{
handler:_defaultHandler,
required:['baz']
}
}
},
{
self:['Click']
},
{
self:{
Click:['baz']
}
}
),
_generateTest(
'When multiple required children for a self event binding are declared, the event is not fired until all the children are added (and order doesn\'t matter)',
{
':Click':{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
},
{
'':{
Click:{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
}
},
{
self:['Click']
},
{
self:{
Click:['bat', 'baf', 'baz']
}
}
),
{
title:'Conditional firing (fireIf)',
test:Uize.push(
Uize.map(
[
{
title:'When fireIf is undefined and widget is disabled, self event is fired',
state:{enabled:false}
},
{
title:'When fireIf is undefined and widget is busy, self event is fired',
state:{busy:true}
},
{
title:'When fireIf evaluates to true, self event is fired',
fireIf:'foo',
state:{foo:true}
}
],
function(_testInfo) {
return {
title:_testInfo.title,
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
':Click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(true);
},
fireIf:_testInfo.fireIf
}
},
null,
null,
_testInfo.state
),
_notFiredTimeout
;
_notFiredTimeout = setTimeout(
function() { _continue(false) },
0
);
_testWidgetClassInstance.fire('Click');
}
};
}
),
[
{
title:'When fireIf evaluates to false, self event isn\'t fired',
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
':Click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(false);
},
fireIf:'foo'
}
},
null,
null
),
_notFiredTimeout
;
// fire child widget event (shouldn't actually be handled by widget)
_testWidgetClassInstance.fire('Click');
_notFiredTimeout = setTimeout(
function() { _continue(true) },
0
);
}
}
]
)
}
]
},
{
title:'Node Events',
test:[
_generateTest(
'When a single node with a single event binding is declared, only that node event is bound',
Uize.lookup(
[
'#foo:click'
],
_defaultHandler
),
{
'#foo':{
click:_defaultHandler
}
},
{
node:{
foo:['click']
}
}
),
_generateTest(
'When a single node with multiple event bindings are declared, only the events for that node are bound',
Uize.lookup(
[
'#foo:click',
'#foo:mousemove',
'#foo:change'
],
_defaultHandler
),
{
'#foo':{
click:_defaultHandler,
mousemove:_defaultHandler,
change:_defaultHandler
}
},
{
node:{
foo:['click', 'mousemove', 'change']
}
}
),
_generateTest(
'When a multiple nodes each with a single event binding are declared, only the one event for each node is bound',
Uize.lookup(
[
'#foo:load',
'#lorem:unload'
],
_defaultHandler
),
{
'#foo':{
load:_defaultHandler
},
'#lorem':{
unload:_defaultHandler
}
},
{
node:{
foo:['load'],
lorem:['unload']
}
}
),
_generateTest(
'When a multiple nodes each with multiple event bindings are declared, only the events for those nodes are bound',
Uize.lookup(
[
'#a:focus',
'#foo:click',
'#lorem:keypress',
'#a:change',
'#foo:mouseover',
'#a:blur',
'#foo:keydown',
'#lorem:keyup',
'#a:load'
],
_defaultHandler
),
{
'#foo':{
click:_defaultHandler,
mouseover:_defaultHandler,
keydown:_defaultHandler
},
'#lorem':{
keypress:_defaultHandler,
keyup:_defaultHandler
},
'#a':{
load:_defaultHandler,
blur:_defaultHandler,
focus:_defaultHandler,
change:_defaultHandler
}
},
{
node:{
foo:['click', 'mouseover', 'keydown'],
lorem:['keypress', 'keyup'],
a:['load', 'blur', 'focus', 'change']
}
}
),
_generateTest(
'When root node with a single event binding is declared, only that node event is bound',
Uize.lookup(
[
'#:click'
],
_defaultHandler
),
{
'#':{
click:_defaultHandler
}
},
{
node:{
'':['click']
}
}
),
_generateTest(
'When root node with multiple event bindings are declared, only the events for the node are bound',
Uize.lookup(
[
'#:click',
'#:mousemove'
],
_defaultHandler
),
{
'#':{
click:_defaultHandler,
mousemove:_defaultHandler
}
},
{
node:{
'':['click', 'mousemove']
}
}
),
{
title:'Required children',
test:[
_generateTest(
'When required is left unspecified for a node event binding, the event is fired normally (w/ no errors)',
{
'#foo:click':{
handler:_defaultHandler
}
},
{
'#foo':{
click:{
handler:_defaultHandler
}
}
},
{
node:{
foo:['click']
}
}
),
_generateTest(
'When a single required child for a node event binding is declared, the event is not fired until the child is added',
{
'#foo:click':{
handler:_defaultHandler,
required:['baz']
}
},
{
'#foo':{
click:{
handler:_defaultHandler,
required:['baz']
}
}
},
{
node:{
foo:['click']
}
},
{
node:{
foo:{
click:['baz']
}
}
}
),
_generateTest(
'When multiple required children for a node event binding are declared, the event is not fired until all the children are added (and order doesn\'t matter)',
{
'#foo:click':{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
},
{
'#foo':{
click:{
handler:_defaultHandler,
required:['baz', 'bat', 'baf']
}
}
},
{
node:{
foo:['click']
}
},
{
node:{
foo:{
click:['bat', 'baf', 'baz']
}
}
}
)
]
},
{
title:'Conditional Firing (fireIf)',
test:Uize.push(
Uize.map(
[
{
title:'When fireIf is unspecified, the wired DOM event doesn\'t fire if busy or disabled'
},
{
title:'When fireIf is null, the wired DOM event doesn\'t fire if busy or disabled',
fireIf:null
},
{
title:'When fireIf is undefined, the wired DOM event doesn\'t fire if busy or disabled',
fireIf:undefined
},
{
title:'When fireIf is empty string, the wired DOM event doesn\'t fire if busy or disabled',
fireIf:''
}
],
function (_eventBindingsInfo) {
return {
title:_eventBindingsInfo.title,
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
'#myNode:click':Uize.copyInto(
{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(false);
}
},
_eventBindingsInfo.fireIf ? {fireIf:_eventBindingsInfo.fireIf} : null
)
},
null,
['myNode']
),
_notFiredTimeout
;
_testWidgetClassInstance.met('wired');
// set widget to busy or disabled
_testWidgetClassInstance.set(
Math.floor(Math.random() * 2)
? {enabled:false}
: {busy:true}
);
// fire DOM node event (shouldn't actually be handled by widget)
_testWidgetClassInstance.getNode('myNode').triggerEvent({
name:'click'
});
_notFiredTimeout = setTimeout(
function() { _continue(true) },
0
);
}
};
}
),
[
{
title:'When fireIf is specified, the wired DOM event doesn\'t fire if the condition hasn\'t been met',
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
'#myNode:click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(false);
},
fireIf:'foo'
}
},
null,
['myNode']
),
_notFiredTimeout
;
_testWidgetClassInstance.met('wired');
// fire DOM node event (shouldn't actually be handled by widget)
_testWidgetClassInstance.getNode('myNode').triggerEvent({
name:'click'
});
_notFiredTimeout = setTimeout(
function() { _continue(true) },
0
);
}
},
{
title:'When fireIf is specified, the wired DOM event fires when the condition has been met',
test:function(_continue) {
var
_testWidgetClassInstance = _getTestWidgetClassInstance(
{
'#myNode:click':{
handler:function() {
clearTimeout(_notFiredTimeout);
_continue(true);
},
fireIf:'foo'
}
},
null,
['myNode']
),
_notFiredTimeout
;
_testWidgetClassInstance.met('wired');
_testWidgetClassInstance.met('foo');
_notFiredTimeout = setTimeout(
function() { _continue(false) },
0
);
// fire DOM node event (shouldn't actually be handled by widget)
_testWidgetClassInstance.getNode('myNode').triggerEvent({
name:'click'
});
}
}
]
)
}
]
},
{
title:'Potential Collision Events',
test:[
_generateTest(
'When events for a child and node with the same name are declared, the proper events are bound',
Uize.lookup(
[
'foo:Click',
'foo:Changed.bar',
'#foo:click',
'#foo:unload',
'#lorem:change',
'lorem:Changed.ipsum'
],
_defaultHandler
),
{
'foo':{
Click:_defaultHandler,
'Changed.bar':_defaultHandler
},
'#foo':{
click:_defaultHandler,
unload:_defaultHandler
},
'#lorem':{
change:_defaultHandler
},
'lorem':{
'Changed.ipsum':_defaultHandler
}
},
{
child:{
'foo':['Click', 'Changed.bar'],
'lorem':['Changed.ipsum']
},
node:{
'foo':['click', 'unload'],
'lorem':['change']
}
}
),
_generateTest(
'When events for a self and root node with the same name are declared, the proper events are bound',
Uize.copyInto(
Uize.lookup(
[
':Click',
'#:click',
'#:unload'
],
_defaultHandler
),
{
':Changed.bar':_defaultChangedHandler
}
),
{
'':{
Click:_defaultHandler,
'Changed.bar':_defaultChangedHandler
},
'#':{
click:_defaultHandler,
unload:_defaultHandler
}
},
{
self:['Click', 'Changed.bar'],
node:{
'':['click', 'unload']
}
}
)
]
},
{
title:'All Event Types',
test:[
_generateTest(
'When all 3 types of events are declared, some with required blocks, the proper events are bound and fire at the right time',
{
':Click':{
handler:_defaultHandler,
required:['green']
},
':Changed.bar':_defaultChangedHandler,
'#:click':_defaultHandler,
'#:unload':_defaultHandler,
'#lorem:change':{
handler:_defaultHandler,
required:['blah']
},
'lorem:Changed.ipsum':_defaultHandler,
'foo:Click':_defaultHandler,
'foo:Changed.bar':{
handler:_defaultHandler,
required:['hiya']
},
'#foo:click':_defaultHandler,
'#foo:unload':_defaultHandler,
'#a:load':_defaultHandler,
'#a:blur':{
handler:_defaultHandler,
required:['red','blue']
},
'#a:focus':_defaultHandler,
'#a:change':_defaultHandler
},
{
'':{
Click:{
handler:_defaultHandler,
required:['green']
},
'Changed.bar':_defaultChangedHandler
},
'#':{
click:_defaultHandler,
unload:_defaultHandler
},
'#lorem':{
change:{
handler:_defaultHandler,
required:['blah']
}
},
'lorem':{
'Changed.ipsum':_defaultHandler
},
'foo':{
Click:_defaultHandler,
'Changed.bar':{
handler:_defaultHandler,
required:['hiya']
}
},
'#foo':{
click:_defaultHandler,
unload:_defaultHandler
},
'#a':{
load:_defaultHandler,
blur:{
handler:_defaultHandler,
required:['red','blue']
},
focus:_defaultHandler,
change:_defaultHandler
}
},
{
self:['Click', 'Changed.bar'],
child:{
'foo':['Click', 'Changed.bar'],
'lorem':['Changed.ipsum']
},
node:{
'':['click', 'unload'],
'foo':['click', 'unload'],
'lorem':['change'],
a:['load', 'blur', 'focus', 'change']
}
},
{
node:{
lorem:{
change:['blah']
},
a:{
blur:['red','blue']
}
},
child:{
foo:{
'Changed.bar':['hiya']
}
},
self:{
Click:['green']
}
}
)
]
},
{
title:'Edge cases',
test:[
{
title:'When a bound child is removed, a fired event on the child is properly handled (no errors & not fired on parent)',
test:function(_continue) {
var
_widget = _getTestWidgetClassInstance(
{
'childA:Click':function() { _continue(false) }
},
['childA']
),
_childToRemove = _widget.children.childA // keep reference so we'll have it after removal
;
_widget.removeChild(_childToRemove);
// fire child event (shouldn't actually be handled by the parent)
_childToRemove.fire('Click');
setTimeout(
function() { _continue(true) },
0
);
}
},
{
title:'When a bound child is removed, and a new same-named child is re-added, a child event is handled by the parent',
test:function(_continue) {
var
_widget = _getTestWidgetClassInstance(
{
'childA:Click':function() {
clearTimeout(_failTimeout);
_continue(true);
}
},
['childA']
),
_failTimeout
;
// remove child
_widget.removeChild('childA');
// add new same-named child
var _newChild = _widget.addChild('childA', Uize.Widget);
// this shouldn't happen because the handler should be rebound when new child was added
_failTimeout = setTimeout(
function() { _continue(false) },
0
);
_newChild.fire('Click'); // fire child event (should be handled by parent)
}
},
{
title:'When a subclass declares the same child/event combination, the base class\' handler is called',
test:function(_continue) {
var
_WidgetClass = _getTestWidgetClass(
{
'childA:Click':function() {
clearTimeout(_failTimeout);
_continue(true);
}
},
['childA']
),
_WidgetSubclass = _WidgetClass.subclass({
eventBindings:{
'childA:Click':function() { }
}
}),
_failTimeout
;
// this shouldn't happen because the handler should be rebound when new child was added
_failTimeout = setTimeout(
function() { _continue(false) },
0
);
_WidgetSubclass().children.childA.fire('Click');
}
}
]
},
{
title:'Restore the global window object',
test:function() {
_global.window = _originalWindow;
return true;
}
}
]
});
}
});