Tests/Specs: Use Chai for assertions, instead of Jasmine's assertions.
[mootools.git] / Specs / Types / Function.js
blob4a86324608dc1de25b803a70eaeb8afe80121fc3
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 MooTools = new String('MooTools');
15 var fn = function(){
16         return Array.from(arguments).slice();
19 var Rules = function(){
20         return this + ' rules';
23 var Args = function(){
24         return [this].concat(Array.prototype.slice.call(arguments));
27 describe("Function Methods", function(){
29         //<1.2compat>
30         // Function.create
31         it('should return a new function', function(){
32                 var fnc = $empty.create();
33                 expect($empty).to.not.equal(fnc);
34         });
36         it('should return a new function with specified argument', function(){
37                 var fnc = fn.create({'arguments': 'rocks'});
38                 expect(fnc()).to.deep.equal(['rocks']);
39         });
41         it('should return a new function with multiple arguments', function(){
42                 var fnc = fn.create({'arguments': ['MooTools', 'rocks']});
43                 expect(fnc()).to.deep.equal(['MooTools', 'rocks']);
44         });
46         it('should return a new function bound to an object', function(){
47                 var fnc = Rules.create({'bind': 'MooTools'});
48                 expect(fnc()).to.equal('MooTools rules');
49         });
51         it('should return a new function as an event', function(){
52                 var fnc = fn.create({'arguments': [0, 1], 'event': true});
53                 expect(fnc('an Event occurred')).to.deep.equal(['an Event occurred', 0, 1]);
54         });
55         //</1.2compat>
57         // Function.bind
59         it('should return the function bound to an object', function(){
60                 var fnc = Rules.bind('MooTools');
61                 expect(fnc()).to.equal('MooTools rules');
62         });
64         it('should return the function bound to an object with specified argument', function(){
65                 var results = Args.bind(MooTools, 'rocks')();
66                 expect(results[0]).to.equal(MooTools);
67                 expect(results[1]).to.equal('rocks');
68         });
70         dit('should return the function bound to an object with multiple arguments', function(){
71                 var results = Args.bind(MooTools, ['rocks', 'da house'])();
72                 expect(results[0]).to.equal(MooTools);
73                 expect(results[1]).to.deep.equal(['rocks', 'da house']);
74         });
76         //<1.2compat>
77         it('should return the function bound to an object with specified argument', function(){
78                 var fnc = Args.bind(MooTools, 'rocks');
79                 var result = fnc();
80                 expect(result[0]).to.equal(MooTools);
81                 expect(result).to.deep.equal([MooTools, 'rocks']);
82         });
84         it('should return the function bound to an object with multiple arguments', function(){
85                 var fnc = Args.bind(MooTools, ['rocks', 'da house']);
86                 var result = fnc();
87                 expect(result[0]).to.equal(MooTools);
88                 expect(result).to.deep.equal([MooTools, 'rocks', 'da house']);
89         });
91         it('should return the function bound to an object and make the function an event listener', function(){
92                 var fnc = Args.bindWithEvent(MooTools);
93                 var result = fnc('an Event occurred');
94                 expect(result[0]).to.equal(MooTools);
95                 expect(result).to.deep.equal([MooTools, 'an Event occurred']);
96         });
98         it('should return the function bound to an object and make the function event listener with multiple arguments', function(){
99                 var fnc = Args.bindWithEvent(MooTools, ['rocks', 'da house']);
100                 var result = fnc('an Event occurred');
101                 expect(result[0]).to.equal(MooTools);
102                 expect(result).to.deep.equal([MooTools, 'an Event occurred', 'rocks', 'da house']);
103         });
104         //</1.2compat>
106         // Function.pass
108         it('should return a function that when called passes the specified arguments to the original function', function(){
109                 var fnc = fn.pass('MooTools is beautiful and elegant');
110                 expect(fnc()).to.deep.equal(['MooTools is beautiful and elegant']);
111         });
113         it('should pass multiple arguments and bind the function to a specific object when it is called', function(){
114                 var fnc = Args.pass(['rocks', 'da house'], MooTools);
115                 var result = fnc();
116                 expect(result[0]).to.equal(MooTools);
117                 expect(result).to.deep.equal([MooTools, 'rocks', 'da house']);
118         });
120         //<1.2compat>
121         // Function.run
122         it('should run the function', function(){
123                 var result = fn.run();
124                 expect(result).to.deep.equal([]);
125         });
127         it('should run the function with multiple arguments', function(){
128                 var result = fn.run(['MooTools', 'beautiful', 'elegant']);
129                 expect(result).to.deep.equal(['MooTools', 'beautiful', 'elegant']);
130         });
132         it('should run the function with multiple arguments and bind the function to an object', function(){
133                 var result = Args.run(['beautiful', 'elegant'], MooTools);
134                 expect(result[0]).to.equal(MooTools);
135                 expect(result).to.deep.equal([MooTools, 'beautiful', 'elegant']);
136         });
137         //</1.2compat>
139         // Function.extend
141         it("should extend the function's properties", function(){
142                 var fnc = (function(){}).extend({a: 1, b: 'c'});
143                 expect(fnc.a).to.equal(1);
144                 expect(fnc.b).to.equal('c');
145         });
148         // Function.attempt
150         it('should call the function without raising an exception', function(){
151                 var fnc = function(){
152                         this_should_not_work();
153                 };
154                 fnc.attempt();
155         });
157         it("should return the function's return value", function(){
158                 var fnc = Function.from('hello world!');
159                 expect(fnc.attempt()).to.equal('hello world!');
160         });
162         it('should return null if the function raises an exception', function(){
163                 var fnc = function(){
164                         this_should_not_work();
165                 };
166                 expect(fnc.attempt()).to.equal(null);
167         });
169         // Function.delay
171         it('delay should return a timer pointer', function(){
172                 var referenceTimer = setTimeout(function(){}, 10000);
173                 var timer = (function(){}).delay(10000);
174                 expect(typeOf(timer)).to.equal(typeOf(referenceTimer));
175                 clearTimeout(timer);
176                 clearTimeout(referenceTimer);
177         });
179         // Function.periodical
181         it('periodical should return a timer pointer', function(){
182                 var referenceTimer = setInterval(function(){}, 10000);
183                 var timer = (function(){}).periodical(10000);
184                 expect(typeOf(timer)).to.equal(typeOf(referenceTimer));
185                 clearInterval(timer);
186                 clearInterval(referenceTimer);
187         });
191 describe('Function.attempt', function(){
193         it('should return the result of the first successful function without executing successive functions', function(){
194                 var calls = 0;
195                 var attempt = Function.attempt(function(){
196                         calls++;
197                         throw new Exception();
198                 }, function(){
199                         calls++;
200                         return 'success';
201                 }, function(){
202                         calls++;
203                         return 'moo';
204                 });
205                 expect(calls).to.equal(2);
206                 expect(attempt).to.equal('success');
207         });
209         it('should return null when no function succeeded', function(){
210                 var calls = 0;
211                 var attempt = Function.attempt(function(){
212                         calls++;
213                         return I_invented_this();
214                 }, function(){
215                         calls++;
216                         return uninstall_ie();
217                 });
218                 expect(calls).to.equal(2);
219                 expect(attempt).to.equal(null);
220         });
224 })();
226 describe('Function.bind', function(){
228         it('should return the function bound to an object', function(){
229                 var spy = sinon.spy();
230                 var f = spy.bind('MooTools');
231                 expect(spy.called).to.equal(false);
232                 f();
233                 expect(spy.calledWith()).to.equal(true);
234                 f('foo', 'bar');
235                 expect(spy.calledWith('foo', 'bar')).to.equal(true);
236         });
238         it('should return the function bound to an object with specified argument', function(){
239                 var binding = {some: 'binding'};
240                 var spy = sinon.stub().returns('something');
241                 var f = spy.bind(binding, 'arg');
243                 expect(spy.called).to.equal(false);
244                 expect(f('additional', 'arguments')).to.equal('something');
245                 expect(spy.lastCall.thisValue).to.equal(binding);
246         });
248         it('should return the function bound to an object with multiple arguments', function(){
249                 var binding = {some: 'binding'};
250                 var spy = sinon.stub().returns('something');
251                 var f = spy.bind(binding, ['foo', 'bar']);
253                 expect(spy.called).to.equal(false);
254                 expect(f('additional', 'arguments')).to.equal('something');
255                 expect(spy.lastCall.thisValue).to.equal(binding);
256         });
258         dit('should still be possible to use it as constructor', function(){
259                 function Alien(type) {
260                         this.type = type;
261                 }
263                 var thisArg = {};
264                 var Tribble = Alien.bind(thisArg, 'Polygeminus grex');
266                 // `thisArg` should **not** be used for the `this` binding when called as a constructor
267                 var fuzzball = new Tribble('Klingon');
268                 expect(fuzzball.type).to.equal('Polygeminus grex');
269         });
271         dit('when using .call(thisArg) on a bound function, it should ignore the thisArg of .call', function(){
272                 var fn = function(){
273                         return [this.foo].concat(Array.slice(arguments));
274                 };
276                 expect(fn.bind({foo: 'bar'})()).to.deep.equal(['bar']);
277                 expect(fn.bind({foo: 'bar'}, 'first').call({foo: 'yeah!'}, 'yooo')).to.deep.equal(['bar', 'first', 'yooo']);
279                 var bound = fn.bind({foo: 'bar'});
280                 var bound2 = fn.bind({foo: 'yep'});
281                 var inst = new bound;
282                 inst.foo = 'noo!!';
283                 expect(bound2.call(inst, 'yoo', 'howdy')).to.deep.equal(['yep', 'yoo', 'howdy']);
284         });
288 describe('Function.pass', function(){
290         it('should return a function that when called passes the specified arguments to the original function', function(){
291                 var spy = sinon.stub().returns('the result');
292                 var fnc = spy.pass('an argument');
293                 expect(spy.called).to.equal(false);
294                 expect(fnc('additional', 'arguments')).to.equal('the result');
295                 expect(spy.calledWith('an argument')).to.equal(true);
296                 expect(spy.callCount).to.equal(1);
297         });
299         it('should pass multiple arguments and bind the function to a specific object when it is called', function(){
300                 var spy = sinon.stub().returns('the result');
301                 var binding = {some: 'binding'};
302                 var fnc = spy.pass(['multiple', 'arguments'], binding);
303                 expect(spy.called).to.equal(false);
304                 expect(fnc('additional', 'arguments')).to.equal('the result');
305                 expect(spy.lastCall.thisValue).to.equal(binding);
306                 expect(spy.calledWith('multiple', 'arguments')).to.equal(true);
307         });
311 describe('Function.extend', function(){
313         it("should extend the function's properties", function(){
314                 var fnc = (function(){}).extend({a: 1, b: 'c'});
315                 expect(fnc.a).to.equal(1);
316                 expect(fnc.b).to.equal('c');
317         });
321 describe('Function.attempt', function(){
323         it('should call the function without raising an exception', function(){
324                 var fnc = function(){
325                         throw 'up';
326                 };
327                 fnc.attempt();
328         });
330         it("should return the function's return value", function(){
331                 var spy = sinon.stub().returns('hello world!');
332                 expect(spy.attempt()).to.equal('hello world!');
333         });
335         it('should return null if the function raises an exception', function(){
336                 var fnc = function(){
337                         throw 'up';
338                 };
339                 expect(fnc.attempt()).to.equal(null);
340         });
344 describe('Function.delay', function(){
346         beforeEach(function(){
347                 this.clock = sinon.useFakeTimers();
348         });
350         afterEach(function(){
351                 this.clock.reset();
352                 this.clock.restore();
353         });
355         it('should return a timer pointer', function(){
356                 var spyA = sinon.spy();
357                 var spyB = sinon.spy();
359                 var timerA = spyA.delay(200);
360                 var timerB = spyB.delay(200);
362                 this.clock.tick(100);
364                 expect(spyA.called).to.equal(false);
365                 expect(spyB.called).to.equal(false);
366                 clearTimeout(timerB);
368                 this.clock.tick(250);
369                 expect(spyA.callCount).to.equal(1);
370                 expect(spyB.callCount).to.equal(0);
371         });
373         it('should pass parameter 0', function(){
374                 var spy = sinon.spy();
375                 spy.delay(50, null, 0);
377                 this.clock.tick(100);
378                 expect(spy.calledWith(0)).to.equal(true);
379         });
381         it('should not pass any argument when no arguments passed', function(){
382                 var argumentCount = null;
383                 var spy = function(){
384                         argumentCount = arguments.length;
385                 };
386                 spy.delay(50);
387                 this.clock.tick(100);
388                 expect(argumentCount).to.equal(0);
389         });
393 describe('Function.periodical', function(){
395         beforeEach(function(){
396                 this.clock = sinon.useFakeTimers();
397         });
399         afterEach(function(){
400                 this.clock.reset();
401                 this.clock.restore();
402         });
404         it('should return an interval pointer', function(){
405                 var spy = sinon.spy();
407                 var interval = spy.periodical(10);
408                 expect(spy.called).to.equal(false);
410                 this.clock.tick(100);
412                 expect(spy.callCount).to.be.greaterThan(2);
413                 expect(spy.callCount).to.be.lessThan(15);
414                 clearInterval(interval);
415                 spy.reset();
416                 expect(spy.called).to.equal(false);
418                 this.clock.tick(100);
420                 expect(spy.called).to.equal(false);
421         });
423         it('should pass parameter 0', function(){
424                 var spy = sinon.spy();
425                 var timer = spy.periodical(10, null, 0);
427                 this.clock.tick(100);
429                 expect(spy.calledWith(0)).to.equal(true);
430                 clearInterval(timer);
431         });
433         it('should not pass any argument when no arguments passed', function(){
434                 var argumentCount = null;
435                 var spy = function(){
436                         argumentCount = arguments.length;
437                 };
438                 var timer = spy.periodical(50);
439                 this.clock.tick(100);
441                 expect(argumentCount).to.equal(0);
442                 clearInterval(timer);
443         });