Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / core / test / unit / spec / lang / Object.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.Object", function(){
16
17     describe("getKeys", function(){
18         var getKeys = Ext.Object.getKeys;
19         it("should return an empty array for a null value", function(){
20             expect(getKeys(null)).toEqual([]);
21         });
22
23         it("should return an empty array for an empty object", function(){
24             expect(getKeys({})).toEqual([]);
25         });
26
27         it("should return all the keys in the object", function(){
28             expect(getKeys({
29                 foo: 1,
30                 bar: 2,
31                 baz: 3
32             })).toEqual(['foo', 'bar', 'baz']);
33         });
34     });
35
36     describe("getValues", function(){
37         var getValues = Ext.Object.getValues;
38         it("should return an empty array for a null value", function(){
39             expect(getValues(null)).toEqual([]);
40         });
41
42         it("should return an empty array for an empty object", function(){
43             expect(getValues({})).toEqual([]);
44         });
45
46         it("should return all the values in the object", function(){
47             expect(getValues({
48                 foo: 1,
49                 bar: 2,
50                 baz: 3
51             })).toEqual([1, 2, 3]);
52         });
53     });
54
55     describe("getKey", function(){
56         var getKey = Ext.Object.getKey;
57
58         it("should return null for a null object", function(){
59             expect(getKey(null, 'foo')).toBeNull();
60         });
61
62         it("should return null for an empty object", function(){
63             expect(getKey({}, 'foo')).toBeNull();
64         });
65
66         it("should return null if the value doesn't exist", function(){
67             expect(getKey({
68                 foo: 1,
69                 bar: 2
70             }, 3)).toBeNull();
71         });
72
73         it("should only do strict matching", function(){
74             expect(getKey({
75                 foo: 1
76             }, '1')).toBeNull();
77         });
78
79         it("should return the correct key if it matches", function(){
80             expect(getKey({
81                 foo: 1
82             }, 1)).toEqual('foo');
83         });
84
85         it("should only return the first matched value", function(){
86             expect(getKey({
87                 bar: 1,
88                 foo: 1
89             }, 1)).toEqual('bar');
90         });
91     });
92
93     describe("each", function(){
94         var each = Ext.Object.each;
95
96         describe("scope/params", function(){
97             it("should execute using the passed scope", function(){
98                 var scope = {},
99                     actual;
100
101                 each({
102                     foo: 1,
103                     bar: 'value',
104                     baz: false
105                 }, function(){
106                     actual = this;
107                 }, scope);
108                 expect(actual).toBe(scope);
109             });
110
111             it("should default the scope to the object", function(){
112                 var o = {
113                     foo: 1,
114                     bar: 'value',
115                     baz: false
116                 }, actual;
117
118                 each(o, function(){
119                     actual = this;
120                 });
121                 expect(actual).toBe(o);
122             });
123
124             it("should execute passing the key value and object", function(){
125                 var keys = [],
126                     values = [],
127                     data = {
128                         foo: 1,
129                         bar: 'value',
130                         baz: false
131                     },
132                     obj;
133
134                 each(data, function(key, value, o){
135                     keys.push(key);
136                     values.push(value);
137                     obj = o;
138                 });
139
140                 expect(keys).toEqual(['foo', 'bar', 'baz']);
141                 expect(values).toEqual([1, 'value', false]);
142                 expect(obj).toBe(data);
143             });
144         });
145
146         describe("stopping", function(){
147             it("should not stop by default", function(){
148                 var count = 0;
149                 each({
150                     a: 1,
151                     b: 2,
152                     c: 3,
153                     d: 4
154                 }, function(){
155                     ++count;
156                 });
157                 expect(count).toEqual(4);
158             });
159
160             it("should only stop if the function returns false", function(){
161                 var count = 0;
162                 each({
163                     a: 1,
164                     b: 2,
165                     c: 3,
166                     d: 4
167                 }, function(){
168                     ++count;
169                     return null;
170                 });
171                 expect(count).toEqual(4);
172             });
173
174             it("should stop immediately when false is returned", function(){
175                 var count = 0;
176                 each({
177                     a: 1,
178                     b: 2,
179                     c: 3,
180                     d: 4
181                 }, function(key){
182                     ++count;
183                     return key != 'b';
184                 });
185                 expect(count).toEqual(2);
186             });
187         });
188     });
189
190     describe("toQueryString", function(){
191         var toQueryString = Ext.Object.toQueryString;
192
193         describe("defaults", function(){
194             it("should return an empty string for a null object", function(){
195                 expect(toQueryString(null)).toEqual('');
196             });
197
198             it("should return an empty string for an empty object", function(){
199                 expect(toQueryString({})).toEqual('');
200             });
201         });
202
203         describe("simple values", function(){
204             it("should separate a property/value by an =", function(){
205                 expect(toQueryString({
206                     foo: 1
207                 })).toEqual('foo=1');
208             });
209
210             it("should separate pairs by an &", function(){
211                 expect(toQueryString({
212                     foo: 1,
213                     bar: 2
214                 })).toEqual('foo=1&bar=2');
215             });
216
217             it("should handle properties with empty values", function(){
218                 expect(toQueryString({
219                     foo: null
220                 })).toEqual('foo=');
221             });
222
223             it("should encode dates", function(){
224                 var d = new Date(2011, 0, 1);
225                 expect(toQueryString({
226                     foo: d
227                 })).toEqual('foo=2011-01-01T00%3A00%3A00');
228             });
229
230             it("should url encode the key", function(){
231                 expect(toQueryString({
232                     'a prop': 1
233                 })).toEqual('a%20prop=1');
234             });
235
236             it("should url encode the value", function(){
237                 expect(toQueryString({
238                     prop: '$300 & 5 cents'
239                 })).toEqual('prop=%24300%20%26%205%20cents');
240             });
241
242             it("should encode both key and value at the same time", function(){
243                expect(toQueryString({
244                    'a prop': '$300'
245                })).toEqual('a%20prop=%24300');
246             });
247         });
248
249         describe("arrays", function(){
250             it("should support an array value", function(){
251                 expect(toQueryString({
252                     foo: [1, 2, 3]
253                 })).toEqual('foo=1&foo=2&foo=3');
254             });
255
256             it("should be able to support multiple arrays", function(){
257                 expect(toQueryString({
258                     foo: [1, 2],
259                     bar: [3, 4]
260                 })).toEqual('foo=1&foo=2&bar=3&bar=4');
261             });
262
263             it("should be able to mix arrays and normal values", function(){
264                 expect(toQueryString({
265                     foo: 'val1',
266                     bar: ['val2', 'val3'],
267                     baz: 'val4'
268                 })).toEqual('foo=val1&bar=val2&bar=val3&baz=val4');
269             });
270         });
271
272         describe("recursive", function() {
273             it("should support both nested arrays and objects", function() {
274                 expect(decodeURIComponent(Ext.Object.toQueryString({
275                     username: 'Jacky',
276                     dateOfBirth: {
277                         day: 1,
278                         month: 2,
279                         year: 1911
280                     },
281                     hobbies: ['coding', 'eating', 'sleeping', [1,2]]
282                 }, true))).toEqual('username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=1&hobbies[3][1]=2')
283             })
284         });
285
286     });
287
288     describe("merge", function(){
289         var merge = Ext.Object.merge;
290
291         describe("simple values", function(){
292             it("should copy over numeric values", function(){
293                 expect(merge({}, 'prop1', 1)).toEqual({
294                     prop1: 1
295                 });
296             });
297
298             it("should copy over string values", function(){
299                 expect(merge({}, 'prop1', 'val')).toEqual({
300                     prop1: 'val'
301                 });
302             });
303
304             it("should copy over boolean values", function(){
305                 expect(merge({}, 'prop1', true)).toEqual({
306                     prop1: true
307                 });
308             });
309
310             it("should copy over null values", function(){
311                 expect(merge({}, 'prop1', null)).toEqual({
312                     prop1: null
313                 });
314             });
315         });
316
317         describe("complex values", function(){
318             it("should copy a simple object but not have the same reference", function(){
319                 var o = {
320                     foo: 'prop',
321                     tada: {
322                         blah: 'bleh'
323                     }
324                 }, result = merge({}, 'prop', o);
325
326                 expect(result.prop).toEqual({
327                     foo: 'prop',
328                     tada: {
329                         blah: 'bleh'
330                     }
331                 });
332                 expect(result.prop).not.toBe(o);
333             });
334
335             it("should NOT merge an instance (the constructor of which is not Object)", function(){
336                 var o = new Ext.Base(),
337                     result = merge({}, 'prop1', o);
338
339                 expect(result.prop1).toBe(o);
340             });
341         });
342
343         describe("overwriting properties", function(){
344             it("should merge objects if an object exists on the source and the passed value is an object literal", function(){
345                 expect(merge({
346                     prop: {
347                         foo: 1
348                     }
349                 }, 'prop', {
350                     bar: 2
351                 })).toEqual({
352                     prop: {
353                         foo: 1,
354                         bar: 2
355                     }
356                 });
357             });
358
359             it("should copy an object reference if an object exists on the source and the passed value is some kind of class", function(){
360                 var o = new Ext.Base(),
361                     result = merge({
362                         prop: {}
363                     }, 'prop', o);
364
365                 expect(result).toEqual({
366                     prop: o
367                 });
368                 expect(result.prop).toBe(o);
369             });
370
371             it("should replace the value of the target object if it is not an object", function(){
372                 var o = new Ext.Base(),
373                     result = merge({
374                         prop: 1
375                     }, 'prop', o);
376
377                 expect(result.prop).toEqual(o);
378                 expect(result.prop).toBe(o);
379             });
380
381             it("should overwrite simple values", function(){
382                 expect(merge({
383                     prop: 1
384                 }, 'prop', 2)).toEqual({
385                     prop: 2
386                 });
387             });
388         });
389
390         describe("merging objects", function(){
391             it("should merge objects", function(){
392                 expect(merge({}, {
393                     foo: 1
394                 })).toEqual({
395                     foo: 1
396                 });
397             });
398
399             it("should merge left to right", function(){
400                 expect(merge({}, {
401                     foo: 1
402                 }, {
403                     foo: 2
404                 }, {
405                     foo: 3
406                 })).toEqual({
407                     foo: 3
408                 })
409             });
410         });
411
412         it("should modify and return the source", function(){
413             var o = {},
414                 result = merge(o, 'foo', 'bar');
415
416             expect(result.foo).toEqual('bar');
417             expect(result).toBe(o);
418
419         });
420     });
421
422     describe("toQueryObjects", function() {
423         var object = {
424             username: 'Jacky',
425             dateOfBirth: {
426                 day: 1,
427                 month: 2,
428                 year: 1911
429             },
430             hobbies: ['coding', 'eating', 'sleeping', [1,2,3]]
431         };
432
433         it("simple key value", function() {
434             expect(Ext.Object.toQueryObjects('username', 'Jacky')).toEqual([
435                 {
436                     name: 'username',
437                     value: 'Jacky'
438                 }
439             ]);
440         });
441
442         it("non-recursive array", function() {
443             expect(Ext.Object.toQueryObjects('hobbies', ['eating', 'sleeping', 'coding'])).toEqual([
444                 {
445                     name: 'hobbies',
446                     value: 'eating'
447                 },
448                 {
449                     name: 'hobbies',
450                     value: 'sleeping'
451                 },
452                 {
453                     name: 'hobbies',
454                     value: 'coding'
455                 }
456             ]);
457         });
458
459         it("recursive object", function() {
460             expect(Ext.Object.toQueryObjects('dateOfBirth', {
461                 day: 1,
462                 month: 2,
463                 year: 1911,
464                 somethingElse: {
465                     nested: {
466                         very: 'very',
467                         deep: {
468                             inHere: true
469                         }
470                     }
471                 }
472             }, true)).toEqual([
473                 {
474                     name: 'dateOfBirth[day]',
475                     value: 1
476                 },
477                 {
478                     name: 'dateOfBirth[month]',
479                     value: 2
480                 },
481                 {
482                     name: 'dateOfBirth[year]',
483                     value: 1911
484                 },
485                 {
486                     name: 'dateOfBirth[somethingElse][nested][very]',
487                     value: 'very'
488                 },
489                 {
490                     name: 'dateOfBirth[somethingElse][nested][deep][inHere]',
491                     value: true
492                 }
493             ]);
494         });
495
496         it("recursive array", function() {
497             expect(Ext.Object.toQueryObjects('hobbies', [
498                 'eating', 'sleeping', 'coding', ['even', ['more']]
499             ], true)).toEqual([
500                 {
501                     name: 'hobbies[0]',
502                     value: 'eating'
503                 },
504                 {
505                     name: 'hobbies[1]',
506                     value: 'sleeping'
507                 },
508                 {
509                     name: 'hobbies[2]',
510                     value: 'coding'
511                 },
512                 {
513                     name: 'hobbies[3][0]',
514                     value: 'even'
515                 },
516                 {
517                     name: 'hobbies[3][1][0]',
518                     value: 'more'
519                 }
520             ]);
521         });
522     });
523
524     describe("fromQueryString", function() {
525         var fromQueryString = Ext.Object.fromQueryString;
526
527         describe("standard mode", function() {
528             it("empty string", function(){
529                 expect(fromQueryString('')).toEqual({});
530             });
531
532             it("simple single key value pair", function(){
533                 expect(fromQueryString('name=Jacky')).toEqual({name: 'Jacky'});
534             });
535
536             it("simple single key value pair with empty value", function(){
537                 expect(fromQueryString('name=')).toEqual({name: ''});
538             });
539
540             it("multiple key value pairs", function(){
541                 expect(fromQueryString('name=Jacky&loves=food')).toEqual({name: 'Jacky', loves: 'food'});
542             });
543
544             it("multiple key value pairs with URI encoded component", function(){
545                 expect(fromQueryString('a%20property=%24300%20%26%205%20cents')).toEqual({'a property': '$300 & 5 cents'});
546             });
547
548             it("simple array", function(){
549                 expect(fromQueryString('foo=1&foo=2&foo=3')).toEqual({foo: ['1', '2', '3']});
550             });
551         });
552
553         describe("recursive mode", function() {
554             it("empty string", function(){
555                 expect(fromQueryString('', true)).toEqual({});
556             });
557
558             it("simple single key value pair", function(){
559                 expect(fromQueryString('name=Jacky', true)).toEqual({name: 'Jacky'});
560             });
561
562             it("simple single key value pair with empty value", function(){
563                 expect(fromQueryString('name=', true)).toEqual({name: ''});
564             });
565
566             it("multiple key value pairs", function(){
567                 expect(fromQueryString('name=Jacky&loves=food', true)).toEqual({name: 'Jacky', loves: 'food'});
568             });
569
570             it("multiple key value pairs with URI encoded component", function(){
571                 expect(fromQueryString('a%20property=%24300%20%26%205%20cents', true)).toEqual({'a property': '$300 & 5 cents'});
572             });
573
574             it("simple array (last value with the same name will overwrite previous value)", function(){
575                 expect(fromQueryString('foo=1&foo=2&foo=3', true)).toEqual({foo: '3'});
576             });
577
578             it("simple array with empty brackets", function(){
579                 expect(fromQueryString('foo[]=1&foo[]=2&foo[]=3', true)).toEqual({foo: ['1', '2', '3']});
580             });
581
582             it("simple array with non-empty brackets", function(){
583                 expect(fromQueryString('foo[0]=1&foo[1]=2&foo[2]=3', true)).toEqual({foo: ['1', '2', '3']});
584             });
585
586             it("simple array with non-empty brackets and non sequential keys", function(){
587                 expect(fromQueryString('foo[3]=1&foo[1]=2&foo[2]=3&foo[0]=0', true)).toEqual({foo: ['0', '2', '3', '1']});
588             });
589
590             it("simple array with non-empty brackets and non sequential keys and holes", function(){
591                 expect(fromQueryString('foo[3]=1&foo[1]=2&foo[2]=3', true)).toEqual({foo: [undefined, '2', '3', '1']});
592             });
593
594             it("nested array", function(){
595                 expect(fromQueryString('some[0][0]=stuff&some[0][1]=morestuff&some[0][]=otherstuff&some[1]=thingelse', true)).toEqual({
596                     some: [
597                         ['stuff', 'morestuff', 'otherstuff'],
598                         'thingelse'
599                     ]
600                 });
601             });
602
603             it("nested object", function(){
604                 expect(fromQueryString('dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&dateOfBirth[extra][hour]=4&dateOfBirth[extra][minute]=30', true)).toEqual({
605                     dateOfBirth: {
606                         day: '1',
607                         month: '2',
608                         year: '1911',
609                         extra: {
610                             hour: '4',
611                             minute: '30'
612                         }
613                     }
614                 });
615             });
616
617             it("nested mixed types", function(){
618                 expect(fromQueryString('username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff', true)).toEqual({
619                     username: 'Jacky',
620                     dateOfBirth: {
621                         day: '1',
622                         month: '2',
623                         year: '1911'
624                     },
625                     hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
626                 });
627             });
628         });
629     });
630
631 });
632