Merge pull request #2789 from SergioCrisostomo/upgrade-karma-sauce-launcher
[mootools.git] / Specs / Element / Element.Event.js
blob29fd183a0aa072ee325e39d0f83ccd5e79ff93d3
1 /*
2 ---
3 name: Element.Event
4 requires: ~
5 provides: ~
6 ...
7 */
9 (function(){
11 var Local = Local || {};
13 var fire = 'fireEvent',
14         create = function(){
15                 return new Element('div');
16         };
18 describe('Events API: Element', function(){
20         beforeEach(function(){
21                 Local.called = 0;
22                 Local.fn = function(){
23                         return Local.called++;
24                 };
25         });
27         it('should add an Event to the Class', function(){
28                 var object = create();
30                 object.addEvent('event', Local.fn)[fire]('event');
32                 expect(Local.called).to.equal(1);
33         });
35         it('should add multiple Events to the Class', function(){
36                 create().addEvents({
37                         event1: Local.fn,
38                         event2: Local.fn
39                 })[fire]('event1')[fire]('event2');
41                 expect(Local.called).to.equal(2);
42         });
44         it('should remove a specific method for an event', function(){
45                 var object = create();
46                 var x = 0, fn = function(){ x++; };
48                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvent('event', Local.fn)[fire]('event');
50                 expect(x).to.equal(1);
51                 expect(Local.called).to.equal(0);
52         });
54         it('should remove an event and its methods', function(){
55                 var object = create();
56                 var x = 0, fn = function(){ x++; };
58                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvents('event')[fire]('event');
60                 expect(x).to.equal(0);
61                 expect(Local.called).to.equal(0);
62         });
64         it('should remove all events', function(){
65                 var object = create();
66                 var x = 0, fn = function(){ x++; };
68                 object.addEvent('event1', Local.fn).addEvent('event2', fn).removeEvents();
69                 object[fire]('event1')[fire]('event2');
71                 // Should not fail.
72                 object.removeEvents()[fire]('event1')[fire]('event2');
74                 expect(x).to.equal(0);
75                 expect(Local.called).to.equal(0);
76         });
78         it('should remove events with an object', function(){
79                 var object = create();
80                 var events = {
81                         event1: Local.fn,
82                         event2: Local.fn
83                 };
85                 object.addEvent('event1', function(){ Local.fn(); }).addEvents(events)[fire]('event1');
86                 expect(Local.called).to.equal(2);
88                 object.removeEvents(events);
89                 object[fire]('event1');
90                 expect(Local.called).to.equal(3);
92                 object[fire]('event2');
93                 expect(Local.called).to.equal(3);
94         });
96         it('should remove an event immediately', function(){
97                 var object = create();
99                 var methods = [];
101                 var three = function(){
102                         methods.push(3);
103                 };
105                 object.addEvent('event', function(){
106                         methods.push(1);
107                         this.removeEvent('event', three);
108                 }).addEvent('event', function(){
109                         methods.push(2);
110                 }).addEvent('event', three);
112                 object[fire]('event');
113                 expect(methods).to.eql([1, 2]);
115                 object[fire]('event');
116                 expect(methods).to.eql([1, 2, 1, 2]);
117         });
119         it('should be able to remove itself', function(){
120                 var object = create();
122                 var methods = [];
124                 var one = function(){
125                         object.removeEvent('event', one);
126                         methods.push(1);
127                 };
128                 var two = function(){
129                         object.removeEvent('event', two);
130                         methods.push(2);
131                 };
132                 var three = function(){
133                         methods.push(3);
134                 };
136                 object.addEvent('event', one).addEvent('event', two).addEvent('event', three);
138                 object[fire]('event');
139                 expect(methods).to.eql([1, 2, 3]);
141                 object[fire]('event');
142                 expect(methods).to.eql([1, 2, 3, 3]);
143         });
147 // Restore native fireEvent in IE for Syn.
148 var createElement = function(tag, props){
149         var el = new Element(tag);
150         if (el._fireEvent) el.fireEvent = el._fireEvent;
151         return el.set(props);
154 describe('Element.Event', function(){
156         it('Should trigger the click event', function(){
157                 var callback = sinon.spy();
159                 var el = createElement('a', {
160                         text: 'test',
161                         styles: {
162                                 display: 'block',
163                                 overflow: 'hidden',
164                                 height: '1px'
165                         },
166                         events: {
167                                 click: callback
168                         }
169                 }).inject(document.body);
171                 syn.trigger(el, 'click');
173                 expect(callback.called).to.equal(true);
174                 el.destroy();
175         });
177         it('Should trigger the click event and prevent the default behavior', function(){
178                 var callback = sinon.spy();
180                 var el = createElement('a', {
181                         text: 'test',
182                         styles: {
183                                 display: 'block',
184                                 overflow: 'hidden',
185                                 height: '1px'
186                         },
187                         events: {
188                                 click: function(event){
189                                         event.preventDefault();
190                                         callback();
191                                 }
192                         }
193                 }).inject(document.body);
195                 syn.trigger(el, 'click');
197                 expect(callback.called).to.equal(true);
198                 el.destroy();
199         });
201         var ddescribe = (window.postMessage && !navigator.userAgent.match(/phantomjs/i)) ? describe : xdescribe;
202         ddescribe('(async)', function(){
204                 beforeEach(function(done){
205                         this.message = null;
206                         this.spy = sinon.spy();
207                         var self = this;
208                         window.addEvent('message', function(e){
209                                 self.message = e.event.data;
210                                 self.spy();
211                         });
212                         window.postMessage('I am a message from outer space...', '*');
213                         setTimeout(done, 150);
214                 });
216                 it('Should trigger a message event', function(){
217                         expect(this.spy.called).to.equal(true);
218                         expect(this.message).to.equal('I am a message from outer space...');
219                 });
221         });
223         it('Should watch for a key-down event', function(){
224                 var callback = sinon.spy();
226                 var div = createElement('div').addEvent('keydown', function(event){
227                         callback(event.key);
228                 }).inject(document.body);
230                 syn.key(div, 'a');
232                 expect(callback.calledWith('a')).to.equal(true);
233                 div.destroy();
234         });
236         it('should clone events of an element', function(){
237                 var calls = 0;
239                 var element = new Element('div').addEvent('click', function(){ calls++; });
240                 element.fireEvent('click');
242                 expect(calls).to.equal(1);
244                 var clone = new Element('div').cloneEvents(element, 'click');
245                 clone.fireEvent('click');
247                 expect(calls).to.equal(2);
249                 element.addEvent('custom', function(){ calls += 2; }).fireEvent('custom');
251                 expect(calls).to.equal(4);
253                 clone.cloneEvents(element);
254                 clone.fireEvent('click');
256                 expect(calls).to.equal(5);
258                 clone.fireEvent('custom');
260                 expect(calls).to.equal(7);
261         });
265 describe('Element.Event', function(){
266         // This is private API. Do not use.
268         it('should pass the name of the custom event to the callbacks', function(){
269                 var callbacks = 0;
270                 var callback = sinon.spy();
272                 var fn = function(anything, type){
273                         expect(type).to.equal('customEvent');
274                         callbacks++;
275                 };
276                 Element.Events.customEvent = {
278                         base: 'click',
280                         condition: function(event, type){
281                                 fn(null, type);
282                                 return true;
283                         },
285                         onAdd: fn,
286                         onRemove: fn
288                 };
290                 var div = createElement('div').addEvent('customEvent', callback).inject(document.body);
292                 syn.trigger(div, 'click');
294                 expect(callback.called).to.equal(true);
295                 div.removeEvent('customEvent', callback).destroy();
296                 expect(callbacks).to.equal(3);
297         });
301 describe('Element.Event.change', function(){
303         it('should not fire "change" for any property', function(){
304                 var callback = sinon.spy();
306                 var radio = new Element('input', {
307                         'type': 'radio',
308                         'class': 'someClass',
309                         'checked': 'checked'
310                 }).addEvent('change', callback).inject(document.body);
312                 radio.removeClass('someClass');
313                 expect(callback.called).to.equal(false);
315                 var checkbox = new Element('input', {
316                         'type': 'checkbox',
317                         'class': 'someClass',
318                         'checked': 'checked'
319                 }).addEvent('change', callback).inject(document.body);
321                 checkbox.removeClass('someClass');
322                 expect(callback.called).to.equal(false);
324                 var text = new Element('input', {
325                         'type': 'text',
326                         'class': 'otherClass',
327                         'value': 'text value'
328                 }).addEvent('change', callback).inject(document.body);
330                 text.removeClass('otherClass');
331                 expect(callback.called).to.equal(false);
333                 [radio, checkbox, text].invoke('destroy');
334         });
338 describe('Element.Event keyup with f<key>', function(){
340         it('should pass event.key == f2 when pressing f2 on keyup and keydown', function(){
341                 var keydown = sinon.spy();
342                 var keyup = sinon.spy();
344                 var div = createElement('div')
345                         .addEvent('keydown', function(event){
346                                 keydown(event.key);
347                         })
348                         .addEvent('keyup', function(event){
349                                 keyup(event.key);
350                         })
351                         .inject(document.body);
353                 syn.trigger(div, 'keydown', 'f2');
354                 syn.trigger(div, 'keyup', 'f2');
356                 expect(keydown.calledWith('f2')).to.equal(true);
357                 expect(keyup.calledWith('f2')).to.equal(true);
359                 div.destroy();
360         });
364 describe('Keypress key code', function(){
366 //<ltIE8>
367         // Return early for IE8- because Syn.js does not fire events.
368         if (!document.addEventListener) return;
369 //</ltIE8>
371         var _input, _key, _shift;
372         DOMEvent.defineKey(33, 'pageup');
374         function keyHandler(e){
375                 _key = e.key;
376                 _shift = !!e.event.shiftKey;
377         }
379         function typeWriter(action, cb){
380                 _input = new Element('input', {
381                         'type': 'text',
382                         'id': 'keyTester'
383                 }).addEvent('keypress', keyHandler).inject(document.body);
385                 setTimeout(function(){
386                         syn.type('keyTester', action);
387                         setTimeout(function(){
388                                 cb(_key, _shift);
389                         }, 50);
390                 }, 1);
391         }
393         afterEach(function(){
394                 _input.removeEvent('keypress', keyHandler).destroy();
395         });
397         it('should return "enter" in event.key', function(done){
398                 typeWriter('[enter]', function(key, shift){
399                         expect(key).to.equal('enter');
400                         expect(shift).to.equal(false);
401                         done();
402                 });
403         });
405         it('should return "1" in event.key', function(done){
406                 typeWriter('1', function(key, shift){
407                         expect(key).to.equal('1');
408                         expect(shift).to.equal(false);
409                         done();
410                 });
411         });
413         it('should return "!" when pressing SHIFT + 1', function(done){
414                 typeWriter('[shift]![shift-up]', function(key, shift){
415                         expect(key).to.equal('!');
416                         expect(shift).to.equal(true);
417                         done();
418                 });
419         });
421         it('should map code 33 correctly with keypress event', function(){
422                 var mock = {type: 'keypress', which: 33, shiftKey: true};
423                 var e = new DOMEvent(mock);
424                 expect(e.key).to.equal('!');
425         });
429 describe('Element.removeEvent', function(){
431         it('should remove the onunload method', function(){
432                 var text;
433                 var handler = function(){ text = 'nope'; };
434                 window.addEvent('unload', handler);
435                 window.removeEvent('unload', handler);
436                 window.fireEvent('unload');
437                 expect(text).to.equal(undefined);
438         });
442 describe('relatedTarget', function(){
444         var outer = new Element('div');
445         new Element('div').inject(outer);
446         ['mouseenter', 'mouseleave', 'mouseover', 'mouseout'].each(function(event, i){
447                 it('should listen to a ' + event + ' event and set the correct relatedTarget', function(){
448                         var mockEvent = {type: event};
449                         mockEvent[(i % 2 == 0 ? 'from' : 'to') + 'Element'] = outer; // Simulate FF that does not set relatedTarget.
451                         var e = new DOMEvent(mockEvent);
452                         expect(e.type).to.equal(event);
453                         expect(e.relatedTarget).to.equal(outer);
454                 });
455         });
459 describe('Mouse wheel', function(){
461         function attachProperties(e, direction){
462                 e.detail = 1 * direction;
463                 e.wheelDelta = 1 * direction;
464                 e.deltaY = -1 * direction;
465         }
467         function dispatchFakeWheel(type, wheelDirection){
469                 var event;
470                 try {
471                         // Firefox
472                         event = document.createEvent('MouseEvents');
473                         event.initMouseEvent(type, true, true, window, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
474                         attachProperties(event, wheelDirection);
475                         window.dispatchEvent(event);
476                 } catch (e){}
478                 try {
479                         // Chrome, PhantomJS, Safari
480                         event = document.createEvent('WheelEvent');
481                         event.initMouseEvent(type, 0, 100, window, 0, 0, 0, 0, null, null, null, null);
482                         attachProperties(event, wheelDirection);
483                         window.dispatchEvent(event);
484                 } catch (e){}
486                 try {
487                         // IE9
488                         event = document.createEvent('HTMLEvents');
489                         event.initEvent(type, true, false);
490                         attachProperties(event, wheelDirection);
491                         window.dispatchEvent(event);
492                 } catch (e){}
494                 try {
495                         // IE10+, Safari
496                         event = document.createEvent('MouseEvents');
497                         event.initEvent(type, true, true);
498                         attachProperties(event, wheelDirection);
499                         window.dispatchEvent(event);
500                 } catch (e){}
502                 try {
503                         // IE8
504                         event = document.createEventObject();
505                         document.documentElement.fireEvent(type, event);
506                 } catch (e){}
507         }
509         var triggered = false;
510         var wheel = false;
511         var testWheel = !!window.addEventListener;
512         var callback = function(e){
513                 if (e.wheel) wheel = e.wheel > 0 ? 'wheel moved up' : 'wheel moved down';
514                 triggered = true;
515         };
517         beforeEach(function(){
518                 wheel = triggered = false;
519                 window.addEvent('mousewheel', callback);
520                 document.documentElement.addEvent('mousewheel', callback);
521         });
523         afterEach(function(){
524                 window.removeEvent('mousewheel', callback);
525                 document.documentElement.removeEvent('mousewheel', callback);
526         });
528         it('should trigger/listen to mousewheel event', function(){
529                 // http://jsfiddle.net/W6QrS/3
531                 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(dispatchFakeWheel);
532                 expect(triggered).to.equal(true);
533         });
535         it('should listen to mouse wheel direction', function(){
536                 // http://jsfiddle.net/58yCr/
538                 if (!testWheel) return;
540                 // Fire event with wheel going up.
541                 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(function(type){
542                         dispatchFakeWheel(type, 120);
543                 });
544                 expect(wheel).to.equal('wheel moved up');
545                 wheel = false;
547                 // Fire event with wheel going down.
548                 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(function(type){
549                         dispatchFakeWheel(type, -120);
550                 });
551                 expect(wheel).to.equal('wheel moved down');
552         });
556 })();