Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / core / test / unit / spec / class / Class.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 describe("Ext.Class", function() {
16     var cls, emptyFn = function(){};
17     beforeEach(function() {
18         window.My = {
19             awesome: {
20                 Class: function(){console.log(11)},
21                 Class1: function(){console.log(12)},
22                 Class2: function(){console.log(13)}
23             },
24             cool: {
25                 AnotherClass: function(){console.log(21)},
26                 AnotherClass1: function(){console.log(22)},
27                 AnotherClass2: function(){console.log(23)}
28             }
29         };
30     });
31
32     afterEach(function() {
33         if (window.My) {
34             window.My = undefined;
35         }
36
37         try {
38             delete window.My;
39         } catch (e) {}
40     });
41
42     // START PREPROCESSORS =================================================================== /
43     describe("preprocessors", function() {
44
45         beforeEach(function() {
46             cls = function() {};
47             cls.prototype.config = {};
48             cls.ownMethod = function(name, fn) {
49                 this.prototype[name] = fn;
50             };
51             cls.addStatics = Ext.Base.addStatics;
52                         cls.addInheritableStatics = Ext.Base.addInheritableStatics;
53         });
54
55         describe("extend", function() {
56
57             it("should extend from Base if no 'extend' property found", function() {
58                 var data = {};
59
60                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
61
62                 expect((new cls) instanceof Ext.Base).toBeTruthy();
63             });
64
65             it("should extend from given parent class", function() {
66                 var data = {
67                     extend: My.awesome.Class
68                 };
69
70                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
71
72                 expect((new cls) instanceof My.awesome.Class).toBeTruthy();
73             });
74
75             it("should have superclass reference", function() {
76                 var data = {
77                     extend: My.awesome.Class
78                 };
79
80                 var parentPrototype = My.awesome.Class.prototype;
81
82                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
83
84                 expect(cls.superclass).toEqual(parentPrototype);
85                 expect((new cls).superclass).toEqual(parentPrototype);
86             });
87         });
88
89         describe("config", function() {
90
91             it("should create getter if not exists", function() {
92                 var data = {
93                     config: {
94                         someName: 'someValue'
95                     }
96                 };
97
98                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
99
100                 expect(data.getSomeName).toBeDefined();
101             });
102
103             it("should NOT create getter if already exists", function() {
104                 var data = {
105                     config: {
106                         someName: 'someValue'
107                     }
108                 };
109
110                 var called = false;
111                 cls.prototype.getSomeName = function() {
112                     called = true;
113                 };
114
115                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
116
117                 expect(data.getSomeName).not.toBeDefined();
118             });
119
120             it("should create setter if not exists", function() {
121                 var data = {
122                     config: {
123                         someName: 'someValue'
124                     }
125                 };
126
127                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
128
129                 expect(data.setSomeName).toBeDefined();
130             });
131
132             it("should NOT create setter if already exists", function() {
133                 var data = {
134                     config: {
135                         someName: 'someValue'
136                     }
137                 };
138
139                 var called = false;
140
141                 cls.prototype.setSomeName = function() {
142                     called = true;
143                 };
144
145                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
146
147                 expect(data.setSomeName).not.toBeDefined();
148             });
149
150             it("should create apply if not exists", function() {
151                 var data = {
152                     config: {
153                         someName: 'someValue'
154                     }
155                 };
156
157                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
158
159                 expect(data.applySomeName).toBeDefined();
160             });
161
162             it("should NOT create apply if already exists", function() {
163                 var data = {
164                     config: {
165                         someName: 'someValue'
166                     }
167                 };
168
169                 var called = false;
170                 cls.prototype.applySomeName = function() {
171                     called = true;
172                 };
173
174                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
175
176                 expect(data.applySomeName).not.toBeDefined();
177             });
178         });
179
180         describe("statics", function() {
181             it("should copy static properties to the class", function() {
182                 var data = {
183                     statics: {
184                         someName: 'someValue',
185                         someMethod: Ext.emptyFn
186                     }
187                 };
188
189                 Ext.Class.preprocessors.statics.fn(cls, data, emptyFn, emptyFn);
190
191                 var obj = new cls;
192
193                 expect(obj.statics).not.toBeDefined();
194                 expect(cls.someName).toBe('someValue');
195                 expect(cls.someMethod).toBe(Ext.emptyFn);
196             });
197         });
198
199         describe("inheritableStatics", function() {
200
201             it("should store names of inheritable static properties", function() {
202                 var data = {
203                     inheritableStatics: {
204                         someName: 'someValue',
205                         someMethod: Ext.emptyFn
206                     }
207                 };
208
209                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
210
211                 var obj = new cls;
212
213                 expect(obj.inheritableStatics).not.toBeDefined();
214                 expect(cls.someName).toBe('someValue');
215                 expect(cls.prototype.$inheritableStatics).toEqual(['someName', 'someMethod']);
216                 expect(cls.someMethod).toBe(Ext.emptyFn);
217             });
218
219             it("should inherit inheritable statics", function() {
220                 var data = {
221                     inheritableStatics: {
222                         someName: 'someValue',
223                         someMethod: Ext.emptyFn
224                     }
225                 }, cls2 = function(){};
226
227                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
228                 Ext.Class.preprocessors.extend.fn(cls2, { extend: cls }, emptyFn, emptyFn);
229
230                 expect(cls2.someName).toEqual('someValue');
231                 expect(cls2.someMethod).toBe(Ext.emptyFn);
232             });
233
234             it("should NOT inherit inheritable statics if the class already has it", function() {
235                 var data = {
236                     inheritableStatics: {
237                         someName: 'someValue',
238                         someMethod: Ext.emptyFn
239                     }
240                 }, cls2 = function(){};
241
242                 cls2.someName = 'someOtherValue';
243                 cls2.someMethod = function(){};
244
245                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
246                 Ext.Class.preprocessors.extend.fn(cls2, { extend: cls }, emptyFn, emptyFn);
247
248                 expect(cls2.someName).toEqual('someOtherValue');
249                 expect(cls2.someMethod).not.toBe(Ext.emptyFn);
250             });
251         });
252     });
253
254     // END PREPROCESSORS =================================================================== /
255
256     describe("Instantiation", function() {
257         var subClass, parentClass, mixinClass1, mixinClass2;
258
259         beforeEach(function() {
260             mixinClass1 = new Ext.Class({
261                 config: {
262                     mixinConfig: 'mixinConfig'
263                 },
264
265                 constructor: function(config) {
266                     this.initConfig(config);
267
268                     this.mixinConstructor1Called = true;
269                 },
270
271                 mixinProperty1: 'mixinProperty1',
272
273                 mixinMethod1: function() {
274                     this.mixinMethodCalled = true;
275                 }
276             });
277
278             mixinClass2 = new Ext.Class({
279                 constructor: function(config) {
280                     this.initConfig(config);
281
282                     this.mixinConstructor2Called = true;
283                 },
284
285                 mixinProperty2: 'mixinProperty2',
286
287                 mixinMethod2: function() {
288                     this.mixinMethodCalled = true;
289                 }
290             });
291
292             parentClass = new Ext.Class({
293                 mixins: {
294                     mixin1: mixinClass1
295                 },
296                 config: {
297                     name: 'parentClass',
298                     isCool: false,
299                     members: {
300                         abe: 'Abraham Elias',
301                         ed: 'Ed Spencer'
302                     },
303                     hobbies: ['football', 'bowling']
304                 },
305                 constructor: function(config) {
306                     this.initConfig(config);
307
308                     this.parentConstructorCalled = true;
309
310                     this.mixins.mixin1.constructor.apply(this, arguments);
311                 },
312
313                 parentProperty: 'parentProperty',
314
315                 parentMethod: function() {
316                     this.parentMethodCalled = true;
317                 }
318             });
319
320             subClass = new Ext.Class({
321                 extend: parentClass,
322                 mixins: {
323                     mixin1: mixinClass1,
324                     mixin2: mixinClass2
325                 },
326                 config: {
327                     name: 'subClass',
328                     isCool: true,
329                     members: {
330                         jacky: 'Jacky Nguyen',
331                         tommy: 'Tommy Maintz'
332                     },
333                     hobbies: ['sleeping', 'eating', 'movies'],
334                     isSpecial: true
335                 },
336                 constructor: function(config) {
337                     this.initConfig(config);
338
339                     this.subConstrutorCalled = true;
340
341                     subClass.superclass.constructor.apply(this, arguments);
342
343                     this.mixins.mixin2.constructor.apply(this, arguments);
344                 },
345                 myOwnMethod: function() {
346                     this.myOwnMethodCalled = true;
347                 }
348             });
349         });
350
351         describe("addStatics", function() {
352             it("single with name - value arguments", function() {
353                 var called = false;
354
355                 subClass.addStatics({
356                     staticMethod: function(){
357                         called = true;
358                     }
359                 });
360
361                 expect(subClass.staticMethod).toBeDefined();
362                 subClass.staticMethod();
363
364                 expect(called).toBeTruthy();
365             });
366
367             it("multiple with object map argument", function() {
368                 subClass.addStatics({
369                     staticProperty: 'something',
370                     staticMethod: function(){}
371                 });
372
373                 expect(subClass.staticProperty).toEqual('something');
374                 expect(subClass.staticMethod).toBeDefined();
375             });
376         });
377
378
379         describe("override", function() {
380             it("should override", function() {
381                 subClass.override({
382                     myOwnMethod: function(){
383                         this.isOverridden = true;
384
385                         this.callOverridden(arguments);
386                     }
387                 });
388
389                 var obj = new subClass;
390                 obj.myOwnMethod();
391
392                 expect(obj.isOverridden).toBe(true);
393                 expect(obj.myOwnMethodCalled).toBe(true);
394             });
395         });
396
397         describe("mixin", function() {
398             it("should have all properties of mixins", function() {
399                 var obj = new subClass;
400                 expect(obj.mixinProperty1).toEqual('mixinProperty1');
401                 expect(obj.mixinProperty2).toEqual('mixinProperty2');
402                 expect(obj.mixinMethod1).toBeDefined();
403                 expect(obj.mixinMethod2).toBeDefined();
404                 expect(obj.config.mixinConfig).toEqual('mixinConfig');
405             });
406         });
407
408         describe("config", function() {
409             it("should merge properly", function() {
410                 var obj = new subClass;
411                 expect(obj.config).toEqual({
412                     mixinConfig: 'mixinConfig',
413                     name: 'subClass',
414                     isCool: true,
415                     members: {
416                         abe: 'Abraham Elias',
417                         ed: 'Ed Spencer',
418                         jacky: 'Jacky Nguyen',
419                         tommy: 'Tommy Maintz'
420                     },
421                     hobbies: ['sleeping', 'eating', 'movies'],
422                     isSpecial: true
423                 });
424             });
425
426             it("should apply default config", function() {
427                 var obj = new subClass;
428                 expect(obj.getName()).toEqual('subClass');
429                 expect(obj.getIsCool()).toEqual(true);
430                 expect(obj.getHobbies()).toEqual(['sleeping', 'eating', 'movies']);
431             });
432
433             it("should apply with supplied config", function() {
434                 var obj = new subClass({
435                     name: 'newName',
436                     isCool: false,
437                     members: {
438                         aaron: 'Aaron Conran'
439                     }
440                 });
441
442                 expect(obj.getName()).toEqual('newName');
443                 expect(obj.getIsCool()).toEqual(false);
444                 expect(obj.getMembers().aaron).toEqual('Aaron Conran');
445             });
446
447             it("should not share the same config", function() {
448                 var obj1 = new subClass({
449                     name: 'newName',
450                     isCool: false,
451                     members: {
452                         aaron: 'Aaron Conran'
453                     }
454                 });
455
456                 var obj2 = new subClass();
457
458                 expect(obj2.getName()).not.toEqual('newName');
459             });
460         });
461
462         describe("overriden methods", function() {
463             it("should call self constructor", function() {
464                 var obj = new subClass;
465                 expect(obj.subConstrutorCalled).toBeTruthy();
466             });
467
468             it("should call parent constructor", function() {
469                 var obj = new subClass;
470                 expect(obj.parentConstructorCalled).toBeTruthy();
471             });
472
473             it("should call mixins constructors", function() {
474                 var obj = new subClass;
475                 expect(obj.mixinConstructor1Called).toBeTruthy();
476                 expect(obj.mixinConstructor2Called).toBeTruthy();
477             });
478         });
479
480     });
481
482 });
483