Improvements after review sugestions
[mootools.git] / Specs / Types / Function.js
blobe89e77e6b9c708716471109bd40c15b95c6c4c62
1 /*
2 ---
3 name: Function
4 requires: ~
5 provides: ~
6 ...
7 */
9 var dit = /*<1.2compat>*/xit || /*</1.2compat>*/it; // don't run unless no compat
11 (function(){
13 var fn = function(){
14         return Array.from(arguments).slice();
17 var Rules = function(){
18         return this + ' rules';
21 var Args = function(){
22         return [this].concat(Array.prototype.slice.call(arguments));
25 describe("Function Methods", function(){
27         //<1.2compat>
28         // Function.create
29         it('should return a new function', function(){
30                 var fnc = $empty.create();
31                 expect($empty === fnc).toBeFalsy();
32         });
34         it('should return a new function', function(){
35                 var fnc = $empty.create();
36                 expect($empty === fnc).toBeFalsy();
37         });
39         it('should return a new function with specified argument', function(){
40                 var fnc = fn.create({'arguments': 'rocks'});
41                 expect(fnc()).toEqual(['rocks']);
42         });
44         it('should return a new function with multiple arguments', function(){
45                 var fnc = fn.create({'arguments': ['MooTools', 'rocks']});
46                 expect(fnc()).toEqual(['MooTools', 'rocks']);
47         });
49         it('should return a new function bound to an object', function(){
50                 var fnc = Rules.create({'bind': 'MooTools'});
51                 expect(fnc()).toEqual('MooTools rules');
52         });
54         it('should return a new function as an event', function(){
55                 var fnc = fn.create({'arguments': [0, 1], 'event': true});
56                 expect(fnc('an Event occurred')).toEqual(['an Event occurred', 0, 1]);
57         });
58         //</1.2compat>
60         // Function.bind
62         it('should return the function bound to an object', function(){
63                 var fnc = Rules.bind('MooTools');
64                 expect(fnc()).toEqual('MooTools rules');
65         });
67         it('should return the function bound to an object with specified argument', function(){
68                 var results = Args.bind('MooTools', 'rocks')();
69                 expect(results[0] + '').toEqual(new String('MooTools') + '');
70                 expect(results[1]).toEqual('rocks');
71         });
73         dit('should return the function bound to an object with multiple arguments', function(){
74                 var results = Args.bind('MooTools', ['rocks', 'da house'])();
75                 expect(results[0] + '').toEqual(new String('MooTools') + '');
76                 expect(results[1]).toEqual(['rocks', 'da house']);
77         });
79         //<1.2compat>
80         it('should return the function bound to an object with specified argument', function(){
81                 var fnc = Args.bind('MooTools', 'rocks');
82                 expect(fnc()).toEqual(['MooTools', 'rocks']);
83         });
85         it('should return the function bound to an object with multiple arguments', function(){
86                 var fnc = Args.bind('MooTools', ['rocks', 'da house']);
87                 expect(fnc()).toEqual(['MooTools', 'rocks', 'da house']);
88         });
90         it('should return the function bound to an object and make the function an event listener', function(){
91                 var fnc = Args.bindWithEvent('MooTools');
92                 expect(fnc('an Event ocurred')).toEqual(['MooTools', 'an Event ocurred']);
93         });
95         it('should return the function bound to an object and make the function event listener with multiple arguments', function(){
96                 var fnc = Args.bindWithEvent('MooTools', ['rocks', 'da house']);
97                 expect(fnc('an Event ocurred')).toEqual(['MooTools', 'an Event ocurred', 'rocks', 'da house']);
98         });
99         //</1.2compat>
101         // Function.pass
103         it('should return a function that when called passes the specified arguments to the original function', function(){
104                 var fnc = fn.pass('MooTools is beautiful and elegant');
105                 expect(fnc()).toEqual(['MooTools is beautiful and elegant']);
106         });
108         it('should pass multiple arguments and bind the function to a specific object when it is called', function(){
109                 var fnc = Args.pass(['rocks', 'da house'], 'MooTools');
110                 expect(fnc()).toEqual(['MooTools', 'rocks', 'da house']);
111         });
113         //<1.2compat>
114         // Function.run
115         it('should run the function', function(){
116                 var result = fn.run();
117                 expect(result).toEqual([]);
118         });
120         it('should run the function with multiple arguments', function(){
121                 var result = fn.run(['MooTools', 'beautiful', 'elegant']);
122                 expect(result).toEqual(['MooTools', 'beautiful', 'elegant']);
123         });
125         it('should run the function with multiple arguments and bind the function to an object', function(){
126                 var result = Args.run(['beautiful', 'elegant'], 'MooTools');
127                 expect(result).toEqual(['MooTools', 'beautiful', 'elegant']);
128         });
129         //</1.2compat>
131         // Function.extend
133         it("should extend the function's properties", function(){
134                 var fnc = (function(){}).extend({a: 1, b: 'c'});
135                 expect(fnc.a).toEqual(1);
136                 expect(fnc.b).toEqual('c');
137         });
140         // Function.attempt
142         it('should call the function without raising an exception', function(){
143                 var fnc = function(){
144                         this_should_not_work();
145                 };
146                 fnc.attempt();
147         });
149         it("should return the function's return value", function(){
150                 var fnc = Function.from('hello world!');
151                 expect(fnc.attempt()).toEqual('hello world!');
152         });
154         it('should return null if the function raises an exception', function(){
155                 var fnc = function(){
156                         this_should_not_work();
157                 };
158                 expect(fnc.attempt()).toBeNull();
159         });
161         // Function.delay
163         it('delay should return a timer pointer', function(){
164                 var timer = (function(){}).delay(10000);
165                 expect(typeOf(timer) == 'number').toBeTruthy();
166                 clearTimeout(timer);
167         });
169         // Function.periodical
171         it('periodical should return a timer pointer', function(){
172                 var timer = (function(){}).periodical(10000);
173                 expect(typeOf(timer) == 'number').toBeTruthy();
174                 clearInterval(timer);
175         });
179 describe('Function.attempt', function(){
181         it('should return the result of the first successful function without executing successive functions', function(){
182                 var calls = 0;
183                 var attempt = Function.attempt(function(){
184                         calls++;
185                         throw new Exception();
186                 }, function(){
187                         calls++;
188                         return 'success';
189                 }, function(){
190                         calls++;
191                         return 'moo';
192                 });
193                 expect(calls).toEqual(2);
194                 expect(attempt).toEqual('success');
195         });
197         it('should return null when no function succeeded', function(){
198                 var calls = 0;
199                 var attempt = Function.attempt(function(){
200                         calls++;
201                         return I_invented_this();
202                 }, function(){
203                         calls++;
204                         return uninstall_ie();
205                 });
206                 expect(calls).toEqual(2);
207                 expect(attempt).toBeNull();
208         });
212 })();
214 describe('Function.bind', function(){
216         it('should return the function bound to an object', function(){
217                 var spy = jasmine.createSpy('Function.bind');
218                 var f = spy.bind('MooTools');
219                 expect(spy).not.toHaveBeenCalled();
220                 f();
221                 expect(spy).toHaveBeenCalledWith();
222                 f('foo', 'bar');
223                 expect(spy).toHaveBeenCalledWith('foo', 'bar');
224         });
226         it('should return the function bound to an object with specified argument', function(){
227                 var binding = {some: 'binding'};
228                 var spy = jasmine.createSpy('Function.bind with arg').andReturn('something');
229                 var f = spy.bind(binding, 'arg');
231                 expect(spy).not.toHaveBeenCalled();
232                 expect(f('additional', 'arguments')).toEqual('something');
233                 expect(spy.mostRecentCall.object).toEqual(binding);
234         });
236         it('should return the function bound to an object with multiple arguments', function(){
237                 var binding = {some: 'binding'};
238                 var spy = jasmine.createSpy('Function.bind with multiple args').andReturn('something');
239                 var f = spy.bind(binding, ['foo', 'bar']);
241                 expect(spy).not.toHaveBeenCalled();
242                 expect(f('additional', 'arguments')).toEqual('something');
243                 expect(spy.mostRecentCall.object).toEqual(binding);
244         });
246         dit('should still be possible to use it as constructor', function(){
247                 function Alien(type) {
248                         this.type = type;
249                 }
251                 var thisArg = {};
252                 var Tribble = Alien.bind(thisArg, 'Polygeminus grex');
254                 // `thisArg` should **not** be used for the `this` binding when called as a constructor
255                 var fuzzball = new Tribble('Klingon');
256                 expect(fuzzball.type).toEqual('Polygeminus grex');
257         });
259         dit('when using .call(thisArg) on a bound function, it should ignore the thisArg of .call', function(){
260                 var fn = function(){
261                         return [this.foo].concat(Array.slice(arguments));
262                 };
264                 expect(fn.bind({foo: 'bar'})()).toEqual(['bar']);
265                 expect(fn.bind({foo: 'bar'}, 'first').call({foo: 'yeah!'}, 'yooo')).toEqual(['bar', 'first', 'yooo']);
267                 var bound = fn.bind({foo: 'bar'});
268                 var bound2 = fn.bind({foo: 'yep'});
269                 var inst = new bound;
270                 inst.foo = 'noo!!';
271                 expect(bound2.call(inst, 'yoo', 'howdy')).toEqual(['yep', 'yoo', 'howdy']);
272         });
276 describe('Function.pass', function(){
278         it('should return a function that when called passes the specified arguments to the original function', function(){
279                 var spy = jasmine.createSpy('Function.pass').andReturn('the result');
280                 var fnc = spy.pass('an argument');
281                 expect(spy).not.toHaveBeenCalled();
282                 expect(fnc('additional', 'arguments')).toBe('the result');
283                 expect(spy).toHaveBeenCalledWith('an argument');
284                 expect(spy.callCount).toBe(1);
285         });
287         it('should pass multiple arguments and bind the function to a specific object when it is called', function(){
288                 var spy = jasmine.createSpy('Function.pass with bind').andReturn('the result');
289                 var binding = {some: 'binding'};
290                 var fnc = spy.pass(['multiple', 'arguments'], binding);
291                 expect(spy).not.toHaveBeenCalled();
292                 expect(fnc('additional', 'arguments')).toBe('the result');
293                 expect(spy.mostRecentCall.object).toEqual(binding);
294                 expect(spy).toHaveBeenCalledWith('multiple', 'arguments');
295         });
299 describe('Function.extend', function(){
301         it("should extend the function's properties", function(){
302                 var fnc = (function(){}).extend({a: 1, b: 'c'});
303                 expect(fnc.a).toEqual(1);
304                 expect(fnc.b).toEqual('c');
305         });
309 describe('Function.attempt', function(){
311         it('should call the function without raising an exception', function(){
312                 var fnc = function(){
313                         throw 'up';
314                 };
315                 fnc.attempt();
316         });
318         it("should return the function's return value", function(){
319                 var spy = jasmine.createSpy('Function.attempt').andReturn('hello world!');
320                 expect(spy.attempt()).toEqual('hello world!');
321         });
323         it('should return null if the function raises an exception', function(){
324                 var fnc = function(){
325                         throw 'up';
326                 };
327                 expect(fnc.attempt()).toBeNull();
328         });
332 describe('Function.delay', function(){
334         beforeEach(function(){
335                 this.clock = sinon.useFakeTimers();
336         });
338         afterEach(function(){
339                 this.clock.reset();
340                 this.clock.restore();
341         });
343         it('should return a timer pointer', function(){
344                 var spyA = jasmine.createSpy('Alice');
345                 var spyB = jasmine.createSpy('Bob');
347                 var timerA = spyA.delay(200);
348                 var timerB = spyB.delay(200);
350                 this.clock.tick(100);
352                 expect(spyA).not.toHaveBeenCalled();
353                 expect(spyB).not.toHaveBeenCalled();
354                 clearTimeout(timerB);
356                 this.clock.tick(250);
357                 expect(spyA.callCount).toBe(1);
358                 expect(spyB.callCount).toBe(0);
359         });
361         it('should pass parameter 0', function(){
362                 var spy = jasmine.createSpy('Function.delay with 0');
363                 spy.delay(50, null, 0);
365                 this.clock.tick(100);
366                 expect(spy).toHaveBeenCalledWith(0);
367         });
369         it('should not pass any argument when no arguments passed', function(){
370                 var argumentCount = null;
371                 var spy = function(){
372                         argumentCount = arguments.length;
373                 };
374                 spy.delay(50);
375                 this.clock.tick(100);
376                 expect(argumentCount).toEqual(0);
377         });
381 describe('Function.periodical', function(){
383         beforeEach(function(){
384                 this.clock = sinon.useFakeTimers();
385         });
387         afterEach(function(){
388                 this.clock.reset();
389                 this.clock.restore();
390         });
392         it('should return an interval pointer', function(){
393                 var spy = jasmine.createSpy('Bond');
395                 var interval = spy.periodical(10);
396                 expect(spy).not.toHaveBeenCalled();
398                 this.clock.tick(100);
400                 expect(spy.callCount).toBeGreaterThan(2);
401                 expect(spy.callCount).toBeLessThan(15);
402                 clearInterval(interval);
403                 spy.reset();
404                 expect(spy).not.toHaveBeenCalled();
406                 this.clock.tick(100);
408                 expect(spy).not.toHaveBeenCalled();
409         });
411         it('should pass parameter 0', function(){
412                 var spy = jasmine.createSpy('Function.periodical with 0');
413                 var timer = spy.periodical(10, null, 0);
415                 this.clock.tick(100);
417                 expect(spy).toHaveBeenCalledWith(0);
418                 clearInterval(timer);
419         });
421         it('should not pass any argument when no arguments passed', function(){
422                 var argumentCount = null;
423                 var spy = function(){
424                         argumentCount = arguments.length;
425                 };
426                 var timer = spy.periodical(50);
427                 this.clock.tick(100);
429                 expect(argumentCount).toEqual(0);
430                 clearInterval(timer);
431         });