| // Copyright 2006 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| goog.provide('goog.objectTest'); |
| goog.setTestOnly('goog.objectTest'); |
| |
| goog.require('goog.functions'); |
| goog.require('goog.object'); |
| goog.require('goog.testing.jsunit'); |
| goog.require('goog.testing.recordFunction'); |
| |
| function stringifyObject(m) { |
| var keys = goog.object.getKeys(m); |
| var s = ''; |
| for (var i = 0; i < keys.length; i++) { |
| s += keys[i] + goog.object.get(m, keys[i]); |
| } |
| return s; |
| } |
| |
| function getObject() { |
| return { |
| a: 0, |
| b: 1, |
| c: 2, |
| d: 3 |
| }; |
| } |
| |
| function testKeys() { |
| var m = getObject(); |
| assertEquals('getKeys, The keys should be a,b,c', |
| 'a,b,c,d', |
| goog.object.getKeys(m).join(',')); |
| } |
| |
| function testValues() { |
| var m = getObject(); |
| assertEquals('getValues, The values should be 0,1,2', |
| '0,1,2,3', goog.object.getValues(m).join(',')); |
| } |
| |
| function testGetAnyKey() { |
| var m = getObject(); |
| assertTrue('getAnyKey, The key should be a,b,c or d', |
| goog.object.getAnyKey(m) in m); |
| assertUndefined('getAnyKey, The key should be undefined', |
| goog.object.getAnyKey({})); |
| } |
| |
| function testGetAnyValue() { |
| var m = getObject(); |
| assertTrue('getAnyValue, The value should be 0,1,2 or 3', |
| goog.object.containsValue(m, goog.object.getAnyValue(m))); |
| assertUndefined('getAnyValue, The value should be undefined', |
| goog.object.getAnyValue({})); |
| } |
| |
| function testContainsKey() { |
| var m = getObject(); |
| assertTrue("containsKey, Should contain the 'a' key", |
| goog.object.containsKey(m, 'a')); |
| assertFalse("containsKey, Should not contain the 'e' key", |
| goog.object.containsKey(m, 'e')); |
| } |
| |
| function testContainsValue() { |
| var m = getObject(); |
| assertTrue('containsValue, Should contain the value 0', |
| goog.object.containsValue(m, 0)); |
| assertFalse('containsValue, Should not contain the value 4', |
| goog.object.containsValue(m, 4)); |
| assertTrue('isEmpty, The map should not be empty', !goog.object.isEmpty(m)); |
| } |
| |
| function testFindKey() { |
| var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}; |
| var key = goog.object.findKey(dict, function(v, k, d) { |
| assertEquals('valid 3rd argument', dict, d); |
| assertTrue('valid 1st argument', goog.object.containsValue(d, v)); |
| assertTrue('valid 2nd argument', k in d); |
| return v % 3 == 0; |
| }); |
| assertEquals('key "c" found', 'c', key); |
| |
| var pred = function(value) { |
| return value > 5; |
| }; |
| assertUndefined('no match', goog.object.findKey(dict, pred)); |
| } |
| |
| function testFindValue() { |
| var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}; |
| var value = goog.object.findValue(dict, function(v, k, d) { |
| assertEquals('valid 3rd argument', dict, d); |
| assertTrue('valid 1st argument', goog.object.containsValue(d, v)); |
| assertTrue('valid 2nd argument', k in d); |
| return k.toUpperCase() == 'C'; |
| }); |
| assertEquals('value 3 found', 3, value); |
| |
| var pred = function(value, key) { |
| return key > 'd'; |
| }; |
| assertUndefined('no match', goog.object.findValue(dict, pred)); |
| } |
| |
| function testClear() { |
| var m = getObject(); |
| goog.object.clear(m); |
| assertTrue('cleared so it should be empty', goog.object.isEmpty(m)); |
| assertFalse("cleared so it should not contain 'a' key", |
| goog.object.containsKey(m, 'a')); |
| } |
| |
| function testClone() { |
| var m = getObject(); |
| var m2 = goog.object.clone(m); |
| assertFalse('clone so it should not be empty', goog.object.isEmpty(m2)); |
| assertTrue("clone so it should contain 'c' key", |
| goog.object.containsKey(m2, 'c')); |
| } |
| |
| function testUnsafeClonePrimitive() { |
| assertEquals('cloning a primitive should return an equal primitive', |
| 5, goog.object.unsafeClone(5)); |
| } |
| |
| function testUnsafeCloneObjectThatHasACloneMethod() { |
| var original = { |
| name: 'original', |
| clone: goog.functions.constant({name: 'clone'}) |
| }; |
| |
| var clone = goog.object.unsafeClone(original); |
| assertEquals('original', original.name); |
| assertEquals('clone', clone.name); |
| } |
| |
| function testUnsafeCloneFlatObject() { |
| var original = {a: 1, b: 2, c: 3}; |
| var clone = goog.object.unsafeClone(original); |
| assertNotEquals(original, clone); |
| assertObjectEquals(original, clone); |
| } |
| |
| function testUnsafeCloneDeepObject() { |
| var original = { |
| a: 1, |
| b: {c: 2, d: 3}, |
| e: {f: {g: 4, h: 5}} |
| }; |
| var clone = goog.object.unsafeClone(original); |
| |
| assertNotEquals(original, clone); |
| assertNotEquals(original.b, clone.b); |
| assertNotEquals(original.e, clone.e); |
| |
| assertEquals(1, clone.a); |
| assertEquals(2, clone.b.c); |
| assertEquals(3, clone.b.d); |
| assertEquals(4, clone.e.f.g); |
| assertEquals(5, clone.e.f.h); |
| } |
| |
| function testUnsafeCloneFunctions() { |
| var original = { |
| f: goog.functions.constant('hi') |
| }; |
| var clone = goog.object.unsafeClone(original); |
| |
| assertNotEquals(original, clone); |
| assertEquals('hi', clone.f()); |
| assertEquals(original.f, clone.f); |
| } |
| |
| function testForEach() { |
| var m = getObject(); |
| var s = ''; |
| goog.object.forEach(m, function(val, key, m2) { |
| assertNotUndefined(key); |
| assertEquals(m, m2); |
| s += key + val; |
| }); |
| assertEquals(s, 'a0b1c2d3'); |
| } |
| |
| function testFilter() { |
| var m = getObject(); |
| |
| var m2 = goog.object.filter(m, function(val, key, m3) { |
| assertNotUndefined(key); |
| assertEquals(m, m3); |
| return val > 1; |
| }); |
| assertEquals(stringifyObject(m2), 'c2d3'); |
| } |
| |
| |
| function testMap() { |
| var m = getObject(); |
| var m2 = goog.object.map(m, function(val, key, m3) { |
| assertNotUndefined(key); |
| assertEquals(m, m3); |
| return val * val; |
| }); |
| assertEquals(stringifyObject(m2), 'a0b1c4d9'); |
| } |
| |
| function testSome() { |
| var m = getObject(); |
| var b = goog.object.some(m, function(val, key, m2) { |
| assertNotUndefined(key); |
| assertEquals(m, m2); |
| return val > 1; |
| }); |
| assertTrue(b); |
| var b = goog.object.some(m, function(val, key, m2) { |
| assertNotUndefined(key); |
| assertEquals(m, m2); |
| return val > 100; |
| }); |
| assertFalse(b); |
| } |
| |
| function testEvery() { |
| var m = getObject(); |
| var b = goog.object.every(m, function(val, key, m2) { |
| assertNotUndefined(key); |
| assertEquals(m, m2); |
| return val >= 0; |
| }); |
| assertTrue(b); |
| b = goog.object.every(m, function(val, key, m2) { |
| assertNotUndefined(key); |
| assertEquals(m, m2); |
| return val > 1; |
| }); |
| assertFalse(b); |
| } |
| |
| function testContains() { |
| var m = getObject(); |
| assertTrue(goog.object.contains(m, 3)); |
| assertFalse(goog.object.contains(m, 4)); |
| } |
| |
| function testObjectProperties() { |
| var m = {}; |
| |
| goog.object.set(m, 'toString', 'once'); |
| goog.object.set(m, 'valueOf', 'upon'); |
| goog.object.set(m, 'eval', 'a'); |
| goog.object.set(m, 'toSource', 'midnight'); |
| goog.object.set(m, 'prototype', 'dreary'); |
| goog.object.set(m, 'hasOwnProperty', 'dark'); |
| |
| assertEquals(goog.object.get(m, 'toString'), 'once'); |
| assertEquals(goog.object.get(m, 'valueOf'), 'upon'); |
| assertEquals(goog.object.get(m, 'eval'), 'a'); |
| assertEquals(goog.object.get(m, 'toSource'), 'midnight'); |
| assertEquals(goog.object.get(m, 'prototype'), 'dreary'); |
| assertEquals(goog.object.get(m, 'hasOwnProperty'), 'dark'); |
| } |
| |
| function testSetDefault() { |
| var dict = {}; |
| assertEquals(1, goog.object.setIfUndefined(dict, 'a', 1)); |
| assertEquals(1, dict['a']); |
| assertEquals(1, goog.object.setIfUndefined(dict, 'a', 2)); |
| assertEquals(1, dict['a']); |
| } |
| |
| function createRecordedGetFoo() { |
| return goog.testing.recordFunction(goog.functions.constant('foo')); |
| } |
| |
| function testSetWithReturnValueNotSet_KeyIsSet() { |
| var f = createRecordedGetFoo(); |
| var obj = {}; |
| obj['key'] = 'bar'; |
| assertEquals( |
| 'bar', |
| goog.object.setWithReturnValueIfNotSet(obj, 'key', f)); |
| f.assertCallCount(0); |
| } |
| |
| function testSetWithReturnValueNotSet_KeyIsNotSet() { |
| var f = createRecordedGetFoo(); |
| var obj = {}; |
| assertEquals( |
| 'foo', |
| goog.object.setWithReturnValueIfNotSet(obj, 'key', f)); |
| f.assertCallCount(1); |
| } |
| |
| function testSetWithReturnValueNotSet_KeySetValueIsUndefined() { |
| var f = createRecordedGetFoo(); |
| var obj = {}; |
| obj['key'] = undefined; |
| assertEquals( |
| undefined, |
| goog.object.setWithReturnValueIfNotSet(obj, 'key', f)); |
| f.assertCallCount(0); |
| } |
| |
| function testTranspose() { |
| var m = getObject(); |
| var b = goog.object.transpose(m); |
| assertEquals('a', b[0]); |
| assertEquals('b', b[1]); |
| assertEquals('c', b[2]); |
| assertEquals('d', b[3]); |
| } |
| |
| function testExtend() { |
| var o = {}; |
| var o2 = {a: 0, b: 1}; |
| goog.object.extend(o, o2); |
| assertEquals(0, o.a); |
| assertEquals(1, o.b); |
| assertTrue('a' in o); |
| assertTrue('b' in o); |
| |
| o2 = {c: 2}; |
| goog.object.extend(o, o2); |
| assertEquals(2, o.c); |
| assertTrue('c' in o); |
| |
| o2 = {c: 3}; |
| goog.object.extend(o, o2); |
| assertEquals(3, o.c); |
| assertTrue('c' in o); |
| |
| o = {}; |
| o2 = {c: 2}; |
| var o3 = {c: 3}; |
| goog.object.extend(o, o2, o3); |
| assertEquals(3, o.c); |
| assertTrue('c' in o); |
| |
| o = {}; |
| o2 = {a: 0, b: 1}; |
| o3 = {c: 2, d: 3}; |
| goog.object.extend(o, o2, o3); |
| assertEquals(0, o.a); |
| assertEquals(1, o.b); |
| assertEquals(2, o.c); |
| assertEquals(3, o.d); |
| assertTrue('a' in o); |
| assertTrue('b' in o); |
| assertTrue('c' in o); |
| assertTrue('d' in o); |
| |
| o = {}; |
| o2 = { |
| 'constructor': 0, |
| 'hasOwnProperty': 1, |
| 'isPrototypeOf': 2, |
| 'propertyIsEnumerable': 3, |
| 'toLocaleString': 4, |
| 'toString': 5, |
| 'valueOf': 6 |
| }; |
| goog.object.extend(o, o2); |
| assertEquals(0, o['constructor']); |
| assertEquals(1, o['hasOwnProperty']); |
| assertEquals(2, o['isPrototypeOf']); |
| assertEquals(3, o['propertyIsEnumerable']); |
| assertEquals(4, o['toLocaleString']); |
| assertEquals(5, o['toString']); |
| assertEquals(6, o['valueOf']); |
| assertTrue('constructor' in o); |
| assertTrue('hasOwnProperty' in o); |
| assertTrue('isPrototypeOf' in o); |
| assertTrue('propertyIsEnumerable' in o); |
| assertTrue('toLocaleString' in o); |
| assertTrue('toString' in o); |
| assertTrue('valueOf' in o); |
| } |
| |
| function testCreate() { |
| assertObjectEquals('With multiple arguments', |
| {a: 0, b: 1}, goog.object.create('a', 0, 'b', 1)); |
| assertObjectEquals('With an array argument', |
| {a: 0, b: 1}, goog.object.create(['a', 0, 'b', 1])); |
| |
| assertObjectEquals('With no arguments', |
| {}, goog.object.create()); |
| assertObjectEquals('With an ampty array argument', |
| {}, goog.object.create([])); |
| |
| assertThrows('Should throw due to uneven arguments', function() { |
| goog.object.create('a'); |
| }); |
| assertThrows('Should throw due to uneven arguments', function() { |
| goog.object.create('a', 0, 'b'); |
| }); |
| assertThrows('Should throw due to uneven length array', function() { |
| goog.object.create(['a']); |
| }); |
| assertThrows('Should throw due to uneven length array', function() { |
| goog.object.create(['a', 0, 'b']); |
| }); |
| } |
| |
| function testCreateSet() { |
| assertObjectEquals('With multiple arguments', |
| {a: true, b: true}, goog.object.createSet('a', 'b')); |
| assertObjectEquals('With an array argument', |
| {a: true, b: true}, goog.object.createSet(['a', 'b'])); |
| |
| assertObjectEquals('With no arguments', |
| {}, goog.object.createSet()); |
| assertObjectEquals('With an ampty array argument', |
| {}, goog.object.createSet([])); |
| } |
| |
| function createTestDeepObject() { |
| var obj = {}; |
| obj.a = {}; |
| obj.a.b = {}; |
| obj.a.b.c = {}; |
| obj.a.b.c.fooArr = [5, 6, 7, 8]; |
| obj.a.b.c.knownNull = null; |
| return obj; |
| } |
| |
| function testGetValueByKeys() { |
| var obj = createTestDeepObject(); |
| assertEquals(obj, goog.object.getValueByKeys(obj)); |
| assertEquals(obj.a, goog.object.getValueByKeys(obj, 'a')); |
| assertEquals(obj.a.b, goog.object.getValueByKeys(obj, 'a', 'b')); |
| assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, 'a', 'b', 'c')); |
| assertEquals(obj.a.b.c.d, |
| goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'd')); |
| assertEquals(8, goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'fooArr', 3)); |
| assertNull(goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'knownNull')); |
| assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g')); |
| } |
| |
| function testGetValueByKeysArraySyntax() { |
| var obj = createTestDeepObject(); |
| assertEquals(obj, goog.object.getValueByKeys(obj, [])); |
| assertEquals(obj.a, goog.object.getValueByKeys(obj, ['a'])); |
| |
| assertEquals(obj.a.b, goog.object.getValueByKeys(obj, ['a', 'b'])); |
| assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, ['a', 'b', 'c'])); |
| assertEquals(obj.a.b.c.d, |
| goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'd'])); |
| assertEquals(8, |
| goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'fooArr', 3])); |
| assertNull(goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'knownNull'])); |
| assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g')); |
| } |
| |
| function testImmutableView() { |
| if (!Object.isFrozen) { |
| return; |
| } |
| var x = {propA: 3}; |
| var y = goog.object.createImmutableView(x); |
| x.propA = 4; |
| x.propB = 6; |
| y.propA = 5; |
| y.propB = 7; |
| assertEquals(4, x.propA); |
| assertEquals(6, x.propB); |
| assertFalse(goog.object.isImmutableView(x)); |
| |
| assertEquals(4, y.propA); |
| assertEquals(6, y.propB); |
| assertTrue(goog.object.isImmutableView(y)); |
| |
| assertFalse('x and y should be different references', x == y); |
| assertTrue( |
| 'createImmutableView should not create a new view of an immutable object', |
| y == goog.object.createImmutableView(y)); |
| } |
| |
| function testImmutableViewStrict() { |
| 'use strict'; |
| |
| // IE9 supports isFrozen, but does not support strict mode. Exit early if we |
| // are not actually running in strict mode. |
| var isStrict = (function() { return !this; })(); |
| |
| if (!Object.isFrozen || !isStrict) { |
| return; |
| } |
| var x = {propA: 3}; |
| var y = goog.object.createImmutableView(x); |
| assertThrows(function() { |
| y.propA = 4; |
| }); |
| assertThrows(function() { |
| y.propB = 4; |
| }); |
| } |
| |
| function testEmptyObjectsAreEqual() { |
| assertTrue(goog.object.equals({}, {})); |
| } |
| |
| function testObjectsWithDifferentKeysAreUnequal() { |
| assertFalse(goog.object.equals({'a': 1}, {'b': 1})); |
| } |
| |
| function testObjectsWithDifferentValuesAreUnequal() { |
| assertFalse(goog.object.equals({'a': 1}, {'a': 2})); |
| } |
| |
| function testObjectsWithSameKeysAndValuesAreEqual() { |
| assertTrue(goog.object.equals({'a': 1}, {'a': 1})); |
| } |
| |
| function testObjectsWithSameKeysInDifferentOrderAreEqual() { |
| assertTrue(goog.object.equals({'a': 1, 'b': 2}, {'b': 2, 'a': 1})); |
| } |