Tests/Specs: Use Chai for assertions, instead of Jasmine's assertions.
[mootools.git] / Specs / Element / Element.Event.js
blobaca7d0e2aa240112861114ebcb3b9106f84d34ec
1 /*
2 ---
3 name: Element.Event
4 requires: ~
5 provides: ~
6 ...
7 */
9 (function(){
11 var Local = Local || {};
13 var fire = 'fireEvent', create = function(){
14         return new Element('div');
17 describe('Events API: Element', function(){
19         beforeEach(function(){
20                 Local.called = 0;
21                 Local.fn = function(){
22                         return Local.called++;
23                 };
24         });
26         it('should add an Event to the Class', function(){
27                 var object = create();
29                 object.addEvent('event', Local.fn)[fire]('event');
31                 expect(Local.called).to.equal(1);
32         });
34         it('should add multiple Events to the Class', function(){
35                 create().addEvents({
36                         event1: Local.fn,
37                         event2: Local.fn
38                 })[fire]('event1')[fire]('event2');
40                 expect(Local.called).to.equal(2);
41         });
43         it('should remove a specific method for an event', function(){
44                 var object = create();
45                 var x = 0, fn = function(){ x++; };
47                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvent('event', Local.fn)[fire]('event');
49                 expect(x).to.equal(1);
50                 expect(Local.called).to.equal(0);
51         });
53         it('should remove an event and its methods', function(){
54                 var object = create();
55                 var x = 0, fn = function(){ x++; };
57                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvents('event')[fire]('event');
59                 expect(x).to.equal(0);
60                 expect(Local.called).to.equal(0);
61         });
63         it('should remove all events', function(){
64                 var object = create();
65                 var x = 0, fn = function(){ x++; };
67                 object.addEvent('event1', Local.fn).addEvent('event2', fn).removeEvents();
68                 object[fire]('event1')[fire]('event2');
70                 // Should not fail
71                 object.removeEvents()[fire]('event1')[fire]('event2');
73                 expect(x).to.equal(0);
74                 expect(Local.called).to.equal(0);
75         });
77         it('should remove events with an object', function(){
78                 var object = create();
79                 var events = {
80                         event1: Local.fn,
81                         event2: Local.fn
82                 };
84                 object.addEvent('event1', function(){ Local.fn(); }).addEvents(events)[fire]('event1');
85                 expect(Local.called).to.equal(2);
87                 object.removeEvents(events);
88                 object[fire]('event1');
89                 expect(Local.called).to.equal(3);
91                 object[fire]('event2');
92                 expect(Local.called).to.equal(3);
93         });
95         it('should remove an event immediately', function(){
96                 var object = create();
98                 var methods = [];
100                 var three = function(){
101                         methods.push(3);
102                 };
104                 object.addEvent('event', function(){
105                         methods.push(1);
106                         this.removeEvent('event', three);
107                 }).addEvent('event', function(){
108                         methods.push(2);
109                 }).addEvent('event', three);
111                 object[fire]('event');
112                 expect(methods).to.deep.equal([1, 2]);
114                 object[fire]('event');
115                 expect(methods).to.deep.equal([1, 2, 1, 2]);
116         });
118         it('should be able to remove itself', function(){
119                 var object = create();
121                 var methods = [];
123                 var one = function(){
124                         object.removeEvent('event', one);
125                         methods.push(1);
126                 };
127                 var two = function(){
128                         object.removeEvent('event', two);
129                         methods.push(2);
130                 };
131                 var three = function(){
132                         methods.push(3);
133                 };
135                 object.addEvent('event', one).addEvent('event', two).addEvent('event', three);
137                 object[fire]('event');
138                 expect(methods).to.deep.equal([1, 2, 3]);
140                 object[fire]('event');
141                 expect(methods).to.deep.equal([1, 2, 3, 3]);
142         });
146 var fragment = document.createDocumentFragment();
148 // Restore native fireEvent in IE for Syn
149 var createElement = function(tag, props){
150         var el = new Element(tag);
151         if (el._fireEvent) el.fireEvent = el._fireEvent;
152         return el.set(props);
155 describe('Element.Event', function(){
157         it('Should trigger the click event', function(){
159                 var callback = sinon.spy();
161                 var el = createElement('a', {
162                         text: 'test',
163                         styles: {
164                                 display: 'block',
165                                 overflow: 'hidden',
166                                 height: '1px'
167                         },
168                         events: {
169                                 click: callback
170                         }
171                 }).inject(document.body);
173                 syn.trigger(el, 'click');
175                 expect(callback.called).to.equal(true);
176                 el.destroy();
177         });
179         it('Should trigger the click event and prevent the default behavior', function(){
181                 var callback = sinon.spy();
183                 var el = createElement('a', {
184                         text: 'test',
185                         styles: {
186                                 display: 'block',
187                                 overflow: 'hidden',
188                                 height: '1px'
189                         },
190                         events: {
191                                 click: function(event){
192                                         event.preventDefault();
193                                         callback();
194                                 }
195                         }
196                 }).inject(document.body);
198                 syn.trigger(el, 'click');
200                 expect(callback.called).to.equal(true);
201                 el.destroy();
203         });
205         var ddescribe = (window.postMessage && !navigator.userAgent.match(/phantomjs/i)) ? describe : xdescribe;
206         ddescribe('(async)', function(){
208                 beforeEach(function(done){
209                         this.message = null;
210                         this.spy = sinon.spy();
211                         var self = this;
212                         window.addEvent('message', function(e){
213                                 self.message = e.event.data;
214                                 self.spy();
215                         });
216                         window.postMessage('I am a message from outer space...', '*');
217                         setTimeout(done, 150);
218                 });
220                 it('Should trigger a message event', function(){
221                         expect(this.spy.called).to.equal(true);
222                         expect(this.message).to.equal('I am a message from outer space...');
223                 });
224         });
226         it('Should watch for a key-down event', function(){
228                 var callback = sinon.spy();
230                 var div = createElement('div').addEvent('keydown', function(event){
231                         callback(event.key);
232                 }).inject(document.body);
234                 syn.key(div, 'a');
236                 expect(callback.calledWith('a')).to.equal(true);
237                 div.destroy();
238         });
240         it('should clone events of an element', function(){
242                 var calls = 0;
244                 var element = new Element('div').addEvent('click', function(){ calls++; });
245                 element.fireEvent('click');
247                 expect(calls).to.equal(1);
249                 var clone = new Element('div').cloneEvents(element, 'click');
250                 clone.fireEvent('click');
252                 expect(calls).to.equal(2);
254                 element.addEvent('custom', function(){ calls += 2; }).fireEvent('custom');
256                 expect(calls).to.equal(4);
258                 clone.cloneEvents(element);
259                 clone.fireEvent('click');
261                 expect(calls).to.equal(5);
263                 clone.fireEvent('custom');
265                 expect(calls).to.equal(7);
266         });
270 describe('Element.Event', function(){
271         // This is private API. Do not use.
273         it('should pass the name of the custom event to the callbacks', function(){
274                 var callbacks = 0;
275                 var callback = sinon.spy();
277                 var fn = function(anything, type){
278                         expect(type).to.equal('customEvent');
279                         callbacks++;
280                 };
281                 Element.Events.customEvent = {
283                         base: 'click',
285                         condition: function(event, type){
286                                 fn(null, type);
287                                 return true;
288                         },
290                         onAdd: fn,
291                         onRemove: fn
293                 };
295                 var div = createElement('div').addEvent('customEvent', callback).inject(document.body);
297                 syn.trigger(div, 'click');
299                 expect(callback.called).to.equal(true);
300                 div.removeEvent('customEvent', callback).destroy();
301                 expect(callbacks).to.equal(3);
302         });
306 describe('Element.Event.change', function(){
308         it('should not fire "change" for any property', function(){
309                 var callback = sinon.spy();
311                 var radio = new Element('input', {
312                         'type': 'radio',
313                         'class': 'someClass',
314                         'checked': 'checked'
315                 }).addEvent('change', callback).inject(document.body);
317                 radio.removeClass('someClass');
318                 expect(callback.called).to.equal(false);
320                 var checkbox = new Element('input', {
321                         'type': 'checkbox',
322                         'class': 'someClass',
323                         'checked': 'checked'
324                 }).addEvent('change', callback).inject(document.body);
326                 checkbox.removeClass('someClass');
327                 expect(callback.called).to.equal(false);
329                 var text = new Element('input', {
330                         'type': 'text',
331                         'class': 'otherClass',
332                         'value': 'text value'
333                 }).addEvent('change', callback).inject(document.body);
335                 text.removeClass('otherClass');
336                 expect(callback.called).to.equal(false);
338                 [radio, checkbox, text].invoke('destroy');
339         });
343 describe('Element.Event keyup with f<key>', function(){
345         it('should pass event.key == f2 when pressing f2 on keyup and keydown', function(){
347                 var keydown = sinon.spy();
348                 var keyup = sinon.spy();
350                 var div = createElement('div')
351                         .addEvent('keydown', function(event){
352                                 keydown(event.key);
353                         })
354                         .addEvent('keyup', function(event){
355                                 keyup(event.key);
356                         })
357                         .inject(document.body);
359                 syn.trigger(div, 'keydown', 'f2');
360                 syn.trigger(div, 'keyup', 'f2');
362                 expect(keydown.calledWith('f2')).to.equal(true);
363                 expect(keyup.calledWith('f2')).to.equal(true);
365                 div.destroy();
367         });
371 describe('Keypress key code', function(){
373         /*<ltIE8>*/
374         // return early for IE8- because Syn.js does not fire events
375         if (!document.addEventListener) return;
376         /*</ltIE8>*/
378         var input, key, shift, done;
379         DOMEvent.defineKey(33, 'pageup');
381         var tests = ['[enter]', '1', '[shift]![shift-up]'];
383         function keyHandler(e){
384                 key = e.key;
385                 shift = !!e.event.shiftKey;
386         }
388         function typeWriter(action){
389                 setTimeout(function () {
390                         syn.type('keyTester', action);
391                 }, 1);
392                 if (done) return true;
393         }
395         beforeEach(function(done){
396                 input = new Element('input', {
397                         'type': 'text',
398                         'id': 'keyTester'
399                 }).addEvent('keypress', keyHandler).inject(document.body);
401                 var test = tests.shift();
402                 if (test != null){
403                         typeWriter(test);
404                         setTimeout(done, 50);
405                 } else {
406                         done();
407                 }
408         });
410         afterEach(function(){
411                 input.removeEvent('keypress', keyHandler).destroy();
412                 input = key = shift = done = null;
413         });
415         it('should return "enter" in event.key', function(){
416                 expect(key).to.equal('enter');
417                 expect(shift).to.equal(false);
418         });
420         it('should return "1" in event.key', function(){
421                 expect(key).to.equal('1');
422                 expect(shift).to.equal(false);
423         });
425         it('should return "!" when pressing SHIFT + 1', function(){
426                 expect(key).to.equal('!');
427                 expect(shift).to.equal(true);
428         });
430         it('should map code 33 correctly with keypress event', function(){
431                 var mock = {type: 'keypress', which: 33, shiftKey: true};
432                 var e = new DOMEvent(mock);
433                 expect(e.key).to.equal('!');
434         });
438 describe('Element.removeEvent', function(){
440         it('should remove the onunload method', function(){
441                 var text;
442                 var handler = function(){ text = 'nope'; };
443                 window.addEvent('unload', handler);
444                 window.removeEvent('unload', handler);
445                 window.fireEvent('unload');
446                 expect(text).to.equal(undefined);
447         });
452 describe('relatedTarget', function () {
454         var outer = new Element('div');
455         var el = new Element('div').inject(outer);
456         ['mouseenter', 'mouseleave', 'mouseover', 'mouseout'].each(function(event, i){
457                 it('should listen to a ' + event + ' event and set the correct relatedTarget', function(){
458                         var mockEvent = {type: event};
459                         mockEvent[(i % 2 == 0 ? 'from' : 'to') + 'Element'] = outer; // simulate FF that does not set relatedTarget
461                         var e = new DOMEvent(mockEvent);
462                         expect(e.type).to.equal(event);
463                         expect(e.relatedTarget).to.equal(outer);
464                 });
465         });
469 describe('Mouse wheel', function(){
471         function attachProperties(e, direction){
472                 e.detail = 1 * direction;
473                 e.wheelDelta = 1 * direction;
474                 e.deltaY = -1 * direction;
475         }
477         function dispatchFakeWheel(type, wheelDirection){
479                 var event;
480                 try {
481                         // Firefox
482                         event = document.createEvent("MouseEvents");
483                         event.initMouseEvent(type, true, true, window, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
484                         attachProperties(event, wheelDirection);
485                         window.dispatchEvent(event);
486                 } catch(e){}
488                 try {
489                         // Chrome, PhantomJS, Safari
490                         event = document.createEvent("WheelEvent");
491                         event.initMouseEvent(type, 0, 100, window, 0, 0, 0, 0, null, null, null, null);
492                         attachProperties(event, wheelDirection);
493                         window.dispatchEvent(event);
494                 } catch(e){}
496                 try {
497                         // IE9
498                         event = document.createEvent("HTMLEvents");
499                         event.initEvent(type, true, false);
500                         attachProperties(event, wheelDirection);
501                         window.dispatchEvent(event);
502                 } catch(e){}
504                 try {
505                         // IE10+, Safari
506                         var event = document.createEvent("MouseEvents");
507                         event.initEvent(type, true, true);
508                         attachProperties(event, wheelDirection);
509                         window.dispatchEvent(event);
510                 } catch(e){}
512                 try {
513                         // IE8
514                         var event = document.createEventObject();
515                         document.documentElement.fireEvent(type, event);
516                 } catch(e){}
517         }
519         var triggered = false;
520         var wheel = false;
521         var testWheel = !!window.addEventListener;
522         var callback = function(e){
523                 if (e.wheel) wheel = e.wheel > 0 ? 'wheel moved up' : 'wheel moved down';
524                 triggered = true;
525         };
527         beforeEach(function(){
528                 wheel = triggered = false;
529                 window.addEvent('mousewheel', callback);
530                 document.documentElement.addEvent('mousewheel', callback);
531         });
533         afterEach(function(){
534                 window.removeEvent('mousewheel', callback);
535                 document.documentElement.removeEvent('mousewheel', callback);
536         });
538         it('should trigger/listen to mousewheel event', function(){
539                 // http://jsfiddle.net/W6QrS/3
541                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(dispatchFakeWheel);
542                 expect(triggered).to.equal(true);
543         });
545         it('should listen to mouse wheel direction', function(){
546                 // http://jsfiddle.net/58yCr/
548                 if (!testWheel) return;
550                 // fire event with wheel going up
551                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(function(type){
552                         dispatchFakeWheel(type, 120);
553                 });
554                 expect(wheel).to.equal('wheel moved up');
555                 wheel = false;
557                 // fire event with wheel going down
558                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(function(type){
559                         dispatchFakeWheel(type, -120);
560                 });
561                 expect(wheel).to.equal('wheel moved down');
562         });
565 })();