diff options
Diffstat (limited to 'js/src/tests/ecma_6/Object/assign.js')
-rw-r--r-- | js/src/tests/ecma_6/Object/assign.js | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/js/src/tests/ecma_6/Object/assign.js b/js/src/tests/ecma_6/Object/assign.js new file mode 100644 index 000000000..5deb09c8e --- /dev/null +++ b/js/src/tests/ecma_6/Object/assign.js @@ -0,0 +1,303 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function checkDataProperty(object, propertyKey, value, writable, enumerable, configurable) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + assertEq(desc === undefined, false); + assertEq('value' in desc, true); + assertEq(desc.value, value); + assertEq(desc.writable, writable); + assertEq(desc.enumerable, enumerable); + assertEq(desc.configurable, configurable); +} + +/* 19.1.2.1 Object.assign ( target, ...sources ) */ +assertEq(Object.assign.length, 2); + +// Basic functionality works with multiple sources +function basicMultipleSources() { + var a = {}; + var b = { bProp : 7 }; + var c = { cProp : 8 }; + Object.assign(a, b, c); + assertEq(a.bProp, 7); + assertEq(a.cProp, 8); +} +basicMultipleSources(); + +// Basic functionality works with symbols (Bug 1052358) +function basicSymbols() { + var a = {}; + var b = { bProp : 7 }; + var aSymbol = Symbol("aSymbol"); + b[aSymbol] = 22; + Object.assign(a, b); + assertEq(a.bProp, 7); + assertEq(a[aSymbol], 22); +} +basicSymbols(); + +// Calls ToObject() for target, skips null/undefined sources, uses +// ToObject(source) otherwise. +function testToObject() { + assertThrowsInstanceOf(() => Object.assign(null, null), TypeError); + assertThrowsInstanceOf(() => Object.assign(), TypeError); + assertThrowsInstanceOf(() => Object.assign(null, {}), TypeError); + assertEq(Object.assign({}, null) instanceof Object, true); + assertEq(Object.assign({}, undefined) instanceof Object, true); + + // Technically an embedding could have this as extension acting differently + // from ours, so a feature-test is inadequate. We can move this subtest + // into extensions/ if that ever matters. + if (typeof objectEmulatingUndefined === "function") { + var falsyObject = objectEmulatingUndefined(); + falsyObject.foo = 7; + + var obj = Object.assign({}, falsyObject); + assertEq(obj instanceof Object, true); + assertEq(obj.foo, 7); + } + + assertEq(Object.assign(true, {}) instanceof Boolean, true); + assertEq(Object.assign(1, {}) instanceof Number, true); + assertEq(Object.assign("string", {}) instanceof String, true); + var o = {}; + assertEq(Object.assign(o, {}), o); +} +testToObject(); + +// Invokes [[OwnPropertyKeys]] on ToObject(source) +function testOwnPropertyKeys() { + assertThrowsInstanceOf(() => Object.assign(null, new Proxy({}, { + getOwnPropertyNames: () => { throw new Error("not called"); } + })), TypeError); + + var ownKeysCalled = false; + Object.assign({}, new Proxy({}, { + ownKeys: function() { + ownKeysCalled = true; + return []; + } + })); + assertEq(ownKeysCalled, true); +}; +testOwnPropertyKeys(); + +// Ensure correct property traversal +function correctPropertyTraversal() { + var log = ""; + var source = new Proxy({a: 1, b: 2}, { + ownKeys: () => ["b", "c", "a"], + getOwnPropertyDescriptor: function(t, pk) { + log += "#" + pk; + return Object.getOwnPropertyDescriptor(t, pk); + }, + get: function(t, pk, r) { + log += "-" + pk; + return t[pk]; + }, + }); + Object.assign({}, source); + assertEq(log, "#b-b#c#a-a"); +} +correctPropertyTraversal(); + +// Only [[Enumerable]] properties are assigned to target +function onlyEnumerablePropertiesAssigned() { + var source = Object.defineProperties({}, { + a: {value: 1, enumerable: true}, + b: {value: 2, enumerable: false}, + }); + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, false); +} +onlyEnumerablePropertiesAssigned(); + + +// Enumerability is decided on-time, not before main loop (1) +function testEnumerabilityDeterminedInLoop1() +{ + var getterCalled = false; + var sourceTarget = { + get a() { getterCalled = true }, + get b() { Object.defineProperty(sourceTarget, "a", {enumerable: false}) }, + }; + var source = new Proxy(sourceTarget, { ownKeys: () => ["b", "a"] }); + Object.assign({}, source); + assertEq(getterCalled, false); +} +testEnumerabilityDeterminedInLoop1(); + +// Enumerability is decided on-time, not before main loop (2) +function testEnumerabilityDeterminedInLoop2() +{ + var getterCalled = false; + var sourceTarget = { + get a() { getterCalled = true }, + get b() { Object.defineProperty(sourceTarget, "a", {enumerable: true}) }, + }; + var source = new Proxy(sourceTarget, { + ownKeys: () => ["b", "a"] + }); + Object.defineProperty(sourceTarget, "a", {enumerable: false}); + Object.assign({}, source); + assertEq(getterCalled, true); +} +testEnumerabilityDeterminedInLoop2(); + +// Properties are retrieved through Get() and assigned onto +// the target as data properties, not in any sense cloned over as descriptors +function testPropertiesRetrievedThroughGet() { + var getterCalled = false; + Object.assign({}, {get a() { getterCalled = true }}); + assertEq(getterCalled, true); +} +testPropertiesRetrievedThroughGet(); + +// Properties are retrieved through Get() +// Properties are assigned through Put() +function testPropertiesAssignedThroughPut() { + var setterCalled = false; + Object.assign({set a(v) { setterCalled = v }}, {a: true}); + assertEq(setterCalled, true); +} +testPropertiesAssignedThroughPut(); + +// Properties are retrieved through Get() +// Properties are assigned through Put(): Existing property attributes are not altered +function propertiesAssignedExistingNotAltered() { + var source = {a: 1, b: 2, c: 3}; + var target = {a: 0, b: 0, c: 0}; + Object.defineProperty(target, "a", {enumerable: false}); + Object.defineProperty(target, "b", {configurable: false}); + Object.defineProperty(target, "c", {enumerable: false, configurable: false}); + Object.assign(target, source); + checkDataProperty(target, "a", 1, true, false, true); + checkDataProperty(target, "b", 2, true, true, false); + checkDataProperty(target, "c", 3, true, false, false); +} +propertiesAssignedExistingNotAltered(); + +// Properties are retrieved through Get() +// Properties are assigned through Put(): Throws TypeError if non-writable +function propertiesAssignedTypeErrorNonWritable() { + var source = {a: 1}; + var target = {a: 0}; + Object.defineProperty(target, "a", {writable: false}); + assertThrowsInstanceOf(() => Object.assign(target, source), TypeError); + checkDataProperty(target, "a", 0, false, true, true); +} +propertiesAssignedTypeErrorNonWritable(); + +// Properties are retrieved through Get() +// Put() creates standard properties; Property attributes from source are ignored +function createsStandardProperties() { + var source = {a: 1, b: 2, c: 3, get d() { return 4 }}; + Object.defineProperty(source, "b", {writable: false}); + Object.defineProperty(source, "c", {configurable: false}); + var target = Object.assign({}, source); + checkDataProperty(target, "a", 1, true, true, true); + checkDataProperty(target, "b", 2, true, true, true); + checkDataProperty(target, "c", 3, true, true, true); + checkDataProperty(target, "d", 4, true, true, true); +} +createsStandardProperties(); + +// Properties created during traversal are not copied +function propertiesCreatedDuringTraversalNotCopied() { + var source = {get a() { this.b = 2 }}; + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, false); +} +propertiesCreatedDuringTraversalNotCopied(); + +// Properties deleted during traversal are not copied +function testDeletePropertiesNotCopied() { + var source = new Proxy({ + get a() { delete this.b }, + b: 2, + }, { + getOwnPropertyNames: () => ["a", "b"] + }); + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, false); +} +testDeletePropertiesNotCopied(); + +function testDeletionExposingShadowedProperty() +{ + var srcProto = { b: 42 }; + var src = + Object.create(srcProto, + { a: { enumerable: true, get: function() { delete this.b; } }, + b: { value: 2, configurable: true, enumerable: true } }); + var source = new Proxy(src, { getOwnPropertyNames: () => ["a", "b"] }); + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, false); +} +testDeletionExposingShadowedProperty(); + +// Properties first deleted and then recreated during traversal are copied (1) +function testDeletedAndRecreatedPropertiesCopied1() { + var source = new Proxy({ + get a() { delete this.c }, + get b() { this.c = 4 }, + c: 3, + }, { + getOwnPropertyNames: () => ["a", "b", "c"] + }); + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, true); + assertEq("c" in target, true); + checkDataProperty(target, "c", 4, true, true, true); +} +testDeletedAndRecreatedPropertiesCopied1(); + +// Properties first deleted and then recreated during traversal are copied (2) +function testDeletedAndRecreatedPropertiesCopied2() { + var source = new Proxy({ + get a() { delete this.c }, + get b() { this.c = 4 }, + c: 3, + }, { + ownKeys: () => ["a", "c", "b"] + }); + var target = Object.assign({}, source); + assertEq("a" in target, true); + assertEq("b" in target, true); + assertEq("c" in target, false); +} +testDeletedAndRecreatedPropertiesCopied2(); + +// String and Symbol valued properties are copied +function testStringAndSymbolPropertiesCopied() { + var keyA = "str-prop"; + var source = {"str-prop": 1}; + var target = Object.assign({}, source); + checkDataProperty(target, keyA, 1, true, true, true); +} +testStringAndSymbolPropertiesCopied(); + +// Intermediate exceptions stop traversal and throw exception +function testExceptionsDoNotStopFirstReported1() { + var TestError = function TestError() {}; + var source = new Proxy({}, { + getOwnPropertyDescriptor: function(t, pk) { + assertEq(pk, "b"); + throw new TestError(); + }, + ownKeys: () => ["b", "a"] + }); + assertThrowsInstanceOf(() => Object.assign({}, source), TestError); +} +testExceptionsDoNotStopFirstReported1(); + + +if (typeof reportCompare == "function") + reportCompare(true, true); |