Upgrade to ExtJS 4.0.2 - Released 06/09/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         });
52
53         describe("extend", function() {
54
55             it("should extend from Base if no 'extend' property found", function() {
56                 var data = {};
57
58                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
59
60                 expect((new cls) instanceof Ext.Base).toBeTruthy();
61             });
62
63             it("should extend from given parent class", function() {
64                 var data = {
65                     extend: My.awesome.Class
66                 };
67
68                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
69
70                 expect((new cls) instanceof My.awesome.Class).toBeTruthy();
71             });
72
73             it("should have superclass reference", function() {
74                 var data = {
75                     extend: My.awesome.Class
76                 };
77
78                 var parentPrototype = My.awesome.Class.prototype;
79
80                 Ext.Class.preprocessors.extend.fn(cls, data, emptyFn, emptyFn);
81
82                 expect(cls.superclass).toEqual(parentPrototype);
83                 expect((new cls).superclass).toEqual(parentPrototype);
84             });
85         });
86
87         describe("config", function() {
88
89             it("should create getter if not exists", function() {
90                 var data = {
91                     config: {
92                         someName: 'someValue'
93                     }
94                 };
95
96                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
97
98                 expect(data.getSomeName).toBeDefined();
99             });
100
101             it("should NOT create getter if already exists", function() {
102                 var data = {
103                     config: {
104                         someName: 'someValue'
105                     }
106                 };
107
108                 var called = false;
109                 cls.prototype.getSomeName = function() {
110                     called = true;
111                 };
112
113                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
114
115                 expect(data.getSomeName).not.toBeDefined();
116             });
117
118             it("should create setter if not exists", function() {
119                 var data = {
120                     config: {
121                         someName: 'someValue'
122                     }
123                 };
124
125                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
126
127                 expect(data.setSomeName).toBeDefined();
128             });
129
130             it("should NOT create setter if already exists", function() {
131                 var data = {
132                     config: {
133                         someName: 'someValue'
134                     }
135                 };
136
137                 var called = false;
138
139                 cls.prototype.setSomeName = function() {
140                     called = true;
141                 };
142
143                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
144
145                 expect(data.setSomeName).not.toBeDefined();
146             });
147
148             it("should create apply if not exists", function() {
149                 var data = {
150                     config: {
151                         someName: 'someValue'
152                     }
153                 };
154
155                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
156
157                 expect(data.applySomeName).toBeDefined();
158             });
159
160             it("should NOT create apply if already exists", function() {
161                 var data = {
162                     config: {
163                         someName: 'someValue'
164                     }
165                 };
166
167                 var called = false;
168                 cls.prototype.applySomeName = function() {
169                     called = true;
170                 };
171
172                 Ext.Class.preprocessors.config.fn(cls, data, emptyFn, emptyFn);
173
174                 expect(data.applySomeName).not.toBeDefined();
175             });
176         });
177
178         describe("statics", function() {
179             it("should copy static properties to the class", function() {
180                 var data = {
181                     statics: {
182                         someName: 'someValue',
183                         someMethod: Ext.emptyFn
184                     }
185                 };
186
187                 Ext.Class.preprocessors.statics.fn(cls, data, emptyFn, emptyFn);
188
189                 var obj = new cls;
190
191                 expect(obj.statics).not.toBeDefined();
192                 expect(cls.someName).toBe('someValue');
193                 expect(cls.someMethod).toBe(Ext.emptyFn);
194             });
195         });
196
197         describe("inheritableStatics", function() {
198
199             it("should store names of inheritable static properties", function() {
200                 var data = {
201                     inheritableStatics: {
202                         someName: 'someValue',
203                         someMethod: Ext.emptyFn
204                     }
205                 };
206
207                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
208
209                 var obj = new cls;
210
211                 expect(obj.inheritableStatics).not.toBeDefined();
212                 expect(cls.someName).toBe('someValue');
213                 expect(cls.prototype.$inheritableStatics).toEqual(['someName', 'someMethod']);
214                 expect(cls.someMethod).toBe(Ext.emptyFn);
215             });
216
217             it("should inherit inheritable statics", function() {
218                 var data = {
219                     inheritableStatics: {
220                         someName: 'someValue',
221                         someMethod: Ext.emptyFn
222                     }
223                 }, cls2 = function(){};
224
225                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
226                 Ext.Class.preprocessors.extend.fn(cls2, { extend: cls }, emptyFn, emptyFn);
227
228                 expect(cls2.someName).toEqual('someValue');
229                 expect(cls2.someMethod).toBe(Ext.emptyFn);
230             });
231
232             it("should NOT inherit inheritable statics if the class already has it", function() {
233                 var data = {
234                     inheritableStatics: {
235                         someName: 'someValue',
236                         someMethod: Ext.emptyFn
237                     }
238                 }, cls2 = function(){};
239
240                 cls2.someName = 'someOtherValue';
241                 cls2.someMethod = function(){};
242
243                 Ext.Class.preprocessors.inheritableStatics.fn(cls, data, emptyFn, emptyFn);
244                 Ext.Class.preprocessors.extend.fn(cls2, { extend: cls }, emptyFn, emptyFn);
245
246                 expect(cls2.someName).toEqual('someOtherValue');
247                 expect(cls2.someMethod).not.toBe(Ext.emptyFn);
248             });
249         });
250     });
251
252     // END PREPROCESSORS =================================================================== /
253
254     describe("Instantiation", function() {
255         var subClass, parentClass, mixinClass1, mixinClass2;
256
257         beforeEach(function() {
258             mixinClass1 = new Ext.Class({
259                 config: {
260                     mixinConfig: 'mixinConfig'
261                 },
262
263                 constructor: function(config) {
264                     this.initConfig(config);
265
266                     this.mixinConstructor1Called = true;
267                 },
268
269                 mixinProperty1: 'mixinProperty1',
270
271                 mixinMethod1: function() {
272                     this.mixinMethodCalled = true;
273                 }
274             });
275
276             mixinClass2 = new Ext.Class({
277                 constructor: function(config) {
278                     this.initConfig(config);
279
280                     this.mixinConstructor2Called = true;
281                 },
282
283                 mixinProperty2: 'mixinProperty2',
284
285                 mixinMethod2: function() {
286                     this.mixinMethodCalled = true;
287                 }
288             });
289
290             parentClass = new Ext.Class({
291                 mixins: {
292                     mixin1: mixinClass1
293                 },
294                 config: {
295                     name: 'parentClass',
296                     isCool: false,
297                     members: {
298                         abe: 'Abraham Elias',
299                         ed: 'Ed Spencer'
300                     },
301                     hobbies: ['football', 'bowling']
302                 },
303                 constructor: function(config) {
304                     this.initConfig(config);
305
306                     this.parentConstructorCalled = true;
307
308                     this.mixins.mixin1.constructor.apply(this, arguments);
309                 },
310
311                 parentProperty: 'parentProperty',
312
313                 parentMethod: function() {
314                     this.parentMethodCalled = true;
315                 }
316             });
317
318             subClass = new Ext.Class({
319                 extend: parentClass,
320                 mixins: {
321                     mixin1: mixinClass1,
322                     mixin2: mixinClass2
323                 },
324                 config: {
325                     name: 'subClass',
326                     isCool: true,
327                     members: {
328                         jacky: 'Jacky Nguyen',
329                         tommy: 'Tommy Maintz'
330                     },
331                     hobbies: ['sleeping', 'eating', 'movies'],
332                     isSpecial: true
333                 },
334                 constructor: function(config) {
335                     this.initConfig(config);
336
337                     this.subConstrutorCalled = true;
338
339                     subClass.superclass.constructor.apply(this, arguments);
340
341                     this.mixins.mixin2.constructor.apply(this, arguments);
342                 },
343                 myOwnMethod: function() {
344                     this.myOwnMethodCalled = true;
345                 }
346             });
347         });
348
349         describe("addStatics", function() {
350             it("single with name - value arguments", function() {
351                 var called = false;
352
353                 subClass.addStatics({
354                     staticMethod: function(){
355                         called = true;
356                     }
357                 });
358
359                 expect(subClass.staticMethod).toBeDefined();
360                 subClass.staticMethod();
361
362                 expect(called).toBeTruthy();
363             });
364
365             it("multiple with object map argument", function() {
366                 subClass.addStatics({
367                     staticProperty: 'something',
368                     staticMethod: function(){}
369                 });
370
371                 expect(subClass.staticProperty).toEqual('something');
372                 expect(subClass.staticMethod).toBeDefined();
373             });
374         });
375
376
377         describe("override", function() {
378             it("should override", function() {
379                 subClass.override({
380                     myOwnMethod: function(){
381                         this.isOverridden = true;
382
383                         this.callOverridden(arguments);
384                     }
385                 });
386
387                 var obj = new subClass;
388                 obj.myOwnMethod();
389
390                 expect(obj.isOverridden).toBe(true);
391                 expect(obj.myOwnMethodCalled).toBe(true);
392             });
393         });
394
395         describe("mixin", function() {
396             it("should have all properties of mixins", function() {
397                 var obj = new subClass;
398                 expect(obj.mixinProperty1).toEqual('mixinProperty1');
399                 expect(obj.mixinProperty2).toEqual('mixinProperty2');
400                 expect(obj.mixinMethod1).toBeDefined();
401                 expect(obj.mixinMethod2).toBeDefined();
402                 expect(obj.config.mixinConfig).toEqual('mixinConfig');
403             });
404         });
405
406         describe("config", function() {
407             it("should merge properly", function() {
408                 var obj = new subClass;
409                 expect(obj.config).toEqual({
410                     mixinConfig: 'mixinConfig',
411                     name: 'subClass',
412                     isCool: true,
413                     members: {
414                         abe: 'Abraham Elias',
415                         ed: 'Ed Spencer',
416                         jacky: 'Jacky Nguyen',
417                         tommy: 'Tommy Maintz'
418                     },
419                     hobbies: ['sleeping', 'eating', 'movies'],
420                     isSpecial: true
421                 });
422             });
423
424             it("should apply default config", function() {
425                 var obj = new subClass;
426                 expect(obj.getName()).toEqual('subClass');
427                 expect(obj.getIsCool()).toEqual(true);
428                 expect(obj.getHobbies()).toEqual(['sleeping', 'eating', 'movies']);
429             });
430
431             it("should apply with supplied config", function() {
432                 var obj = new subClass({
433                     name: 'newName',
434                     isCool: false,
435                     members: {
436                         aaron: 'Aaron Conran'
437                     }
438                 });
439
440                 expect(obj.getName()).toEqual('newName');
441                 expect(obj.getIsCool()).toEqual(false);
442                 expect(obj.getMembers().aaron).toEqual('Aaron Conran');
443             });
444
445             it("should not share the same config", function() {
446                 var obj1 = new subClass({
447                     name: 'newName',
448                     isCool: false,
449                     members: {
450                         aaron: 'Aaron Conran'
451                     }
452                 });
453
454                 var obj2 = new subClass();
455
456                 expect(obj2.getName()).not.toEqual('newName');
457             });
458         });
459
460         describe("overriden methods", function() {
461             it("should call self constructor", function() {
462                 var obj = new subClass;
463                 expect(obj.subConstrutorCalled).toBeTruthy();
464             });
465
466             it("should call parent constructor", function() {
467                 var obj = new subClass;
468                 expect(obj.parentConstructorCalled).toBeTruthy();
469             });
470
471             it("should call mixins constructors", function() {
472                 var obj = new subClass;
473                 expect(obj.mixinConstructor1Called).toBeTruthy();
474                 expect(obj.mixinConstructor2Called).toBeTruthy();
475             });
476         });
477
478     });
479
480 });
481