//------------------------------------------------------------------------------------------------------- | |
// Copyright (C) Microsoft. All rights reserved. | |
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. | |
//------------------------------------------------------------------------------------------------------- | |
if (typeof(WScript) != "undefined") { | |
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); | |
} | |
var tests = { | |
// Note: each test has name (string) and body (function) properties. | |
// Success is when the body does not throw, failure -- when it throws. | |
test01: { | |
name: "formal arg: simple: verify connection: named vs indexed arg", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
var val1 = 2; | |
a = val1; | |
assert.areEqual(val1, a, "wrong value of named parameter (val1)"); | |
assert.areEqual(val1, arguments[0], "wrong value of indexed parameter (val1)"); | |
var val2 = 3; | |
arguments[0] = val2; | |
assert.areEqual(val2, arguments[0], "wrong value of indexed parameter (val2)"); | |
assert.areEqual(val2, a, "wrong value of named parameter (val2)"); | |
} | |
f(passedValue); | |
} | |
}, | |
test02: { | |
name: "formal arg: defineProperty, check property descriptor", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
var val = 2; | |
Object.defineProperty(arguments, 0, { configurable: false, enumerable: false, value: val }); | |
// Note that we expect writable: true because this was omitted in defineProperty above | |
// which is actually re-define property with all attributes == true. | |
var expected = { configurable: false, enumerable: false, writable: true, value: val }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor"); | |
assert.areEqual(val, a, "wrong value of named parameter"); | |
} | |
f(passedValue); | |
} | |
}, | |
test03: { | |
name: "formal arg: defineProperty, set writable to false, verify writability and lost connection. WOOB 1128023", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { writable: false }); | |
var expected = { configurable: true, enumerable: true, writable: false, value: passedValue }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor"); | |
// Attempt to change arguments[0] which is not writable now. | |
var val1 = 2; | |
arguments[0] = val1; | |
assert.areEqual(passedValue, arguments[0], "non-writable changed"); | |
assert.areEqual(passedValue, a, "non-writable changed: named arg also changed"); | |
// Change named arg value, verify we are in connection named vs indexed arg. | |
var val2 = 3; | |
a = val2; | |
assert.areEqual(val2, a, "Attemp to change named arg: didn't work"); | |
assert.areEqual(passedValue, arguments[0], "At this time we should not be connected, but we are"); | |
} | |
f(passedValue); | |
} | |
}, | |
test04: { | |
name: "formal arg: defineProperty, set writable to false AND set value, verify that value changed in both named and indexed arg and that the item was disconnected", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
var val1 = 2; | |
var val2 = 3; | |
Object.defineProperty(arguments, 0, { writable: false, value: val1 }); | |
var expected = { configurable: true, enumerable: true, writable: false, value: val1 }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor"); | |
assert.areEqual(val1, arguments[0], "value: arguments[0]"); | |
assert.areEqual(val1, a, "value: a"); | |
// Verify we are disconnected now. | |
a = val2; | |
assert.areEqual(val2, a, "new value: a"); | |
assert.areEqual(val1, arguments[0], "value: arguments[0] -- did not get disconnected!"); | |
} | |
f(passedValue); | |
} | |
}, | |
test05: { | |
name: "formal arg: defineProperty w/o cause of disconnect, verify still connected to named arg", | |
body: function () { | |
var passedValue = 1; | |
var val1 = 2; | |
var val2 = 3; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { value: val1 }); | |
a = val1; | |
assert.areEqual(val1, arguments[0], "arguments[0] got disconnected"); | |
arguments[0] = val2; | |
assert.areEqual(val2, a, "a got disconnected"); | |
} | |
f(passedValue); | |
} | |
}, | |
test06: { | |
name: "formal arg: defineProperty, disconnect arg[0], verify that arg[1] is still connected", | |
body: function () { | |
function f(a, b) { | |
Object.defineProperty(arguments, 0, { writable: false }); | |
var val1 = 3; | |
var val2 = 4; | |
arguments[1] = val1; | |
assert.areEqual(val1, b, "arg[1] got disconnected"); | |
b = val2; | |
assert.areEqual(val2, arguments[1], "arg[1] got disconnected"); | |
} | |
f(1, 2); | |
} | |
}, | |
test07: { | |
name: "formal arg: defineProperty: convert to accessor property", | |
body: function () { | |
function f(a) { | |
var isGetterFired = false; | |
var isSetterFired = false; | |
Object.defineProperty(arguments, 0, { | |
get: function() { isGetterFired = true; return this.value; }, | |
set: function(arg) { isSetterFired = true; this.value = arg; } | |
}); | |
assert.areEqual(undefined, arguments[0], "unexpected arg[0] value right after conversion to accessor property"); | |
assert.areEqual(true, isGetterFired, "isGetterFired (1)"); | |
isGetterFired = false; | |
var val1 = 2; | |
arguments[0] = val1; | |
assert.areEqual(true, isSetterFired, "isSetterFired"); | |
assert.areEqual(val1, arguments[0], "get value after set"); | |
assert.areEqual(true, isGetterFired, "isGetterFired (2)"); | |
} | |
f(1); | |
} | |
}, | |
test08: { | |
name: "formal arg: defineProperty: convert to accessor, then to data property, verify value and that connection is lost", | |
body: function () { | |
var passedValue = 1; | |
var val1 = 2; | |
var val2 = 3; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { | |
get: function() { return this.value; }, | |
set: function(arg) { this.value = arg; } | |
}); | |
Object.defineProperty(arguments, 0, { value: val1 }); | |
a = val2; | |
assert.areEqual(arguments[0], val1, "arguments[0]"); | |
assert.areNotEqual(arguments[0], a, "arguments[0] != a"); | |
} | |
f(passedValue); | |
} | |
}, | |
test09: { | |
name: "formal arg: defineProperty: force convert to ES5 version but keep connected, check enumeration", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { enumerable: true }); | |
var accumulator = ""; | |
for (var i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual("0: " + passedValue + ";" , accumulator, "accumulator"); | |
} | |
f(passedValue); | |
} | |
}, | |
test10: { | |
name: "formal arg: defineProperty: set non-enumerable/non-writable/delete, check enumeration", | |
body: function () { | |
var passedValue1 = 2; | |
var passedValue2 = 4; | |
function f(a, b, c, d) { | |
Object.defineProperty(arguments, 0, { enumerable: false }); // arguments[0].enumerable = false. | |
Object.defineProperty(arguments, 1, { writable: false }); // arguments[1].writable = false -> disconnected. | |
delete arguments[2]; // arguments[2] is deleted. | |
var i, accumulator = ""; | |
for (i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
// Note that we expect [1].enumerable = true because this was omitted in defineProperty above | |
// which is actually re-define property that previously already had enumerable = true. | |
assert.areEqual("1: " + passedValue1 + ";" + "3: " + passedValue2 + ";", accumulator, "accumulator"); | |
} | |
f(1, passedValue1, 3, passedValue2); | |
} | |
}, | |
test11: { | |
name: "passed/undeclared arg: verify there is no correlation with Object.prototype indexed data properties. WOOB 1143896", | |
body: function () { | |
var passedValue = "passed"; | |
Object.defineProperty(Object.prototype, 0, { value: "from proto", configurable: true, writable: false }); | |
try { | |
function f() { return arguments; } | |
var argObj = f(passedValue); | |
assert.areEqual(passedValue, argObj[0]); | |
} finally { | |
delete Object.prototype[0]; | |
} | |
} | |
}, | |
test12: { | |
name: "formal arg: verify there is no correlation with Object.prototype indexed properties", | |
body: function () { | |
var passedValue = "passed"; | |
Object.defineProperty(Object.prototype, 0, { value: "from proto", configurable: true, writable: false }); | |
try { | |
function f(a) { return arguments } | |
var argObj = f(passedValue); | |
assert.areEqual(passedValue, argObj[0]); | |
} finally { | |
delete Object.prototype[0]; | |
} | |
} | |
}, | |
test13: { | |
name: "passed/undeclared arg: verify there is no correlation with Object.prototype indexed accessor properties. WOOB 1144602", | |
body: function () { | |
var initial = "initial"; | |
var passedValue = "passed"; | |
var data = initial; | |
Object.defineProperty(Object.prototype, 0, { | |
configurable: true, | |
get: function() { return data; }, | |
set: function(arg) { data = arg; } | |
}); | |
try { | |
function f() { return arguments; } | |
var argObj = f(passedValue); | |
assert.areEqual(initial, data, "data: should not be changed as setter on prototype should not be fired"); | |
assert.areEqual(passedValue, argObj[0], "argObj[0]"); | |
} finally { | |
delete Object.prototype[0]; | |
} | |
} | |
}, | |
test14: { | |
name: "formal arg: verify there is no correlation with Object.prototype indexed accessor properties", | |
body: function () { | |
var initial = "initial"; | |
var passedValue = "passed"; | |
var data = initial; | |
Object.defineProperty(Object.prototype, 0, { | |
configurable: true, | |
get: function() { return data; }, | |
set: function(arg) { data = arg; } | |
}); | |
try { | |
function f(a) { return arguments; } | |
var argObj = f(passedValue); | |
assert.areEqual(initial, data, "data: should not be changed as setter on prototype should not be fired"); | |
assert.areEqual(passedValue, argObj[0], "argObj[0]"); | |
} finally { | |
delete Object.prototype[0]; | |
} | |
} | |
}, | |
test15: { | |
name: "formal arg: delete, make sure it's deleted", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { enumerable: false }); // Force convert to ES5 version. | |
delete arguments[0]; | |
assert.areEqual(undefined, arguments[0], "was not deleted."); | |
assert.areEqual(passedValue, a, "a is changed."); | |
} | |
f(passedValue); | |
} | |
}, | |
test16: { | |
name: "formal arg: delete, add, check named arg is not changed", | |
body: function () { | |
var passedValue = 1; | |
function f(a, b) { | |
Object.defineProperty(arguments, 0, { enumerable: false }); // Force convert to ES5 version. | |
delete arguments[0]; | |
arguments[0] = passedValue + 1; | |
assert.areEqual(passedValue, a, "a is changed."); | |
} | |
f(passedValue, 2); | |
} | |
}, | |
test17: { | |
name: "formal arg: delete, then defineProperty with attributes for data property, check the value", | |
body: function () { | |
var passedValue = 1; | |
function f(a) { | |
delete arguments[0]; | |
var val = 2; | |
Object.defineProperty(arguments, 0, { enumerable: true, configurable: true, writable: true, value: val }); | |
assert.areEqual(val, arguments[0], "wrong value"); | |
} | |
f(passedValue); | |
} | |
}, | |
test18: { | |
name: "formal arg: delete, then defineProperty with attributes for accessor property, check the enumeration", | |
body: function () { | |
var passedValue = 1; | |
var getter = function() {return this.value; }; | |
var setter = function(arg) { this.value = arg; }; | |
function f(a) { | |
delete arguments[0]; | |
Object.defineProperty(arguments, 0, { enumerable: true, configurable: true, get: getter, set: setter }); | |
var expected = { configurable: true, enumerable: true, get: getter, set: setter }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong descriptor"); | |
var accumulator = ""; | |
for (var i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual("0: " + undefined + ";", accumulator, "accumulator 2"); | |
} | |
f(passedValue); | |
} | |
}, | |
test19: { | |
name: "formal arg, es5 heap arguments: delete, add, check enumerable/order", | |
body: function () { | |
var passedValue1 = 1; | |
var passedValue2 = 2; | |
var newValue1 = 100; | |
var newValue2 = 200; | |
var i, accumulator; | |
function f(a, b) { | |
// Scenario 1: delete prior to converting to ES5 version. | |
delete arguments[0]; // Delete [0] prior to conversion to ES5. | |
Object.defineProperty(arguments, 0, { configurable: true, enumerable: true, value: newValue1 }); // Bring back [0] by defineProperty. Now args is ES5. | |
accumulator = ""; | |
for (i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual("0: " + newValue1 + ";" + "1: " + passedValue2 + ";", accumulator, "accumulator 1"); | |
// Scenario 2: delete after converting to ES5 version. | |
Object.defineProperty(arguments, 0, { configurable: true, enumerable: true, writable: true, value: newValue1 }); // Bring back [0] by defineProperty. Now args is ES5. | |
delete arguments[0]; // Delete [0] prior after conversion to ES5. | |
arguments[0] = newValue2; // Bring back [0] by setting value. | |
accumulator = ""; | |
for (i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual("0: " + newValue2 + ";" + "1: " + passedValue2 + ";", accumulator, "accumulator 2"); | |
} | |
f(passedValue1, passedValue2); | |
} | |
}, | |
test20: { | |
name: "formal arg, es5 heap arguments: delete, add, keep another arg in objectArray and use one non-formal, check enumerable/order", | |
body: function () { | |
var passedValue1 = 1; | |
var passedValue2 = 2; | |
var passedValue3 = 3; | |
var passedValue4 = 4; | |
var newValue = 100; | |
function f(a, b, c) { | |
Object.defineProperty(arguments, 0, { enumerable: true }); // Add objectArray item | |
Object.defineProperty(arguments, 2, { enumerable: true }); // Add objectArray item | |
var accumulator = ""; | |
delete arguments[0]; | |
arguments[0] = newValue; | |
for (var i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual( | |
"0: " + newValue + ";" + "1: " + passedValue2 + ";" + "2: " + passedValue3 + ";" + "3: " + passedValue4 + ";", | |
accumulator, | |
"accumulator"); | |
} | |
f(passedValue1, passedValue2, passedValue3, passedValue4); | |
} | |
}, | |
test21: { | |
name: "formal arg: defineProperty, set enumerable to false, check getOwnPropertyNames", | |
body: function (a, b) { | |
function f(a) { | |
Object.defineProperty(arguments, 0, { enumerable: false }); | |
// Note: Object.getOwnPropertyNames returns all properties, even non-enumerable. | |
var actual = Object.getOwnPropertyNames(arguments); | |
var expected = { 0: "0", 1: "1", 2: "length", 3: "callee" }; | |
assert.areEqual(expected, actual, "wrong property names"); | |
} | |
f(101, 102); | |
} | |
}, | |
test22Helper: function test22Helper(isConvertNeeded, messagePrefix) { | |
function mkerr(message) { | |
return messagePrefix + ": " + message; | |
} | |
var passedValue = 1; | |
var newPropertyName = "x"; | |
function f(a, b) { | |
if (isConvertNeeded) { | |
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version. | |
} | |
Object.preventExtensions(arguments); // No new properties can be added. | |
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible")); | |
try { | |
Object.defineProperty(arguments, newPropertyName, { enumerable: true, value: 100 }); // add new property | |
assert.fail(mkerr("did not throw exception")); | |
} catch (ex) { | |
} | |
arguments[newPropertyName] = 100; | |
assert.areEqual(undefined, arguments[newPropertyName], mkerr("New property was added after preventExtensions was called")); | |
} | |
f(passedValue, passedValue + 1); | |
}, | |
test22_1: { | |
name: "arguments (non-ES5 version): call Object.preventExtensions, try add new property by defineProperty and direct set", | |
body: function () { | |
tests.test22Helper(false, "non-ES5 version"); | |
} | |
}, | |
test22_2: { | |
name: "arguments (ES5 version): call Object.preventExtensions, try add new property by defineProperty and direct set", | |
body: function () { | |
tests.test22Helper(true, "ES5 version"); | |
} | |
}, | |
test23Helper: function test23Helper(isConvertNeeded, messagePrefix) { | |
function mkerr(message) { | |
return messagePrefix + ": " + message; | |
} | |
var passedValue = 1; | |
function f(a, b) { | |
if (isConvertNeeded) { | |
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version. | |
} | |
Object.preventExtensions(arguments); // This causes configurable, writable = false for all properties + Object.preventExtensions. | |
// Note: formals existed prior to calling Object.preventExtensions, thus they are still modifiable. | |
assert.areEqual(false, Object.isExtensible(arguments), "isExtensible"); | |
var actual = Object.getOwnPropertyDescriptor(arguments, 0); | |
var expected = { configurable: true, enumerable: true, writable: true, value: passedValue }; | |
assert.areEqual(expected, actual, mkerr("wrong descriptor - initial")); | |
// Try to modify/re-configure | |
// Note: do not change value here as it causes different code path than exercised by identified issue. | |
Object.defineProperty(arguments, 0, { enumerable: false }); | |
Object.defineProperty(arguments, 0, { writable: false }); | |
Object.defineProperty(arguments, 0, { configurable: false }); | |
var expected = { configurable: false, enumerable: false, writable: false, value: passedValue }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), mkerr("wrong descriptor - after redefine")); | |
} | |
f(passedValue, passedValue + 1); | |
}, | |
// After Object.preventExtensions(arguments) we can't modify the attributes on formals. | |
test23_1: { | |
name: "arguments (non-ES5 version): call Object.preventExtensions, make sure we can still modify atttibutes on formals without changing the value", | |
body: function () { | |
tests.test23Helper(false, "non-ES5 version"); | |
} | |
}, | |
// After Object.preventExtensions(arguments) we can't modify the attributes on formals. | |
test23_2: { | |
name: "arguments (ES5 version): call Object.preventExtensions, make sure we can still modify atttibutes on formals without changing the value", | |
body: function () { | |
tests.test23Helper(true, "ES5 version"); | |
} | |
}, | |
test24Helper: function test24Helper(isConvertNeeded, messagePrefix) { | |
function mkerr(message) { | |
return messagePrefix + ": " + message; | |
} | |
var passedValue = 1; | |
function f(a, b) { | |
if (isConvertNeeded) { | |
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version. | |
} | |
Object.seal(arguments); // This causes configurable = false for all properties + Object.preventExtensions. | |
assert.areEqual(true, Object.isSealed(arguments), mkerr("isSealed")); | |
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible")); | |
var actual = Object.getOwnPropertyDescriptor(arguments, 0); | |
var expected = { configurable: false, enumerable: true, writable: true, value: passedValue }; | |
assert.areEqual(expected, actual, mkerr("wrong descriptor")); | |
} | |
f(passedValue, passedValue + 1); | |
}, | |
// Object.freeze(arguments -- not ES5 version) does not set configurable to false on formals. | |
test24_1: { | |
name: "arguments (non-ES5 version): call Object.seal, verify descriptor on formal", | |
body: function () { | |
tests.test24Helper(false, "non-ES5 version"); | |
} | |
}, | |
test24_2: { | |
name: "arguments (ES5 version): call Object.seal, verify descriptor on formal", | |
body: function () { | |
tests.test24Helper(true, "ES5 version"); | |
} | |
}, | |
test25Helper: function test25Helper(isConvertNeeded, messagePrefix) { | |
function mkerr(message) { | |
return messagePrefix + ": " + message; | |
} | |
var passedValue = 1; | |
function f(a, b) { | |
if (isConvertNeeded) { | |
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version. | |
} | |
Object.freeze(arguments); // This causes configurable AND writable = false for all properties + Object.preventExtensions. | |
assert.areEqual(true, Object.isFrozen(arguments), mkerr("isFrozen")); | |
assert.areEqual(true, Object.isSealed(arguments), mkerr("isSealed")); | |
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible")); | |
var actual = Object.getOwnPropertyDescriptor(arguments, 0); | |
var expected = { configurable: false, enumerable: true, writable: false, value: passedValue }; | |
assert.areEqual(expected, actual, mkerr("wrong descriptor")); | |
} | |
f(passedValue, passedValue + 1); | |
}, | |
// Object.freeze(arguments -- not ES5 version) does not set configurable and writable to false on formals. | |
test25_1: { | |
name: "arguments (non-ES5 version): call Object.freeze, verify descriptor on formal", | |
body: function () { | |
tests.test25Helper(false, "non-ES5 version"); | |
} | |
}, | |
test25_2: { | |
name: "arguments (ES5 version): call Object.freeze, verify descriptor on formal", | |
body: function () { | |
tests.test25Helper(true, "ES5 version"); | |
} | |
}, | |
test26: { | |
name: "formal arg: delete, preventExtensions, enumerate, make sure the item is deleted", | |
body: function () { | |
var passedValue1 = 1; | |
var passedValue2 = 2; | |
function f(a, b) { | |
delete arguments[1]; | |
Object.preventExtensions(arguments); | |
var accumulator = ""; | |
for (var i in arguments) { | |
accumulator += i.toString() + ": " + arguments[i] + ";"; | |
} | |
assert.areEqual("0: " + passedValue1 + ";", accumulator, "accumulator"); | |
assert.areEqual(undefined, arguments[1], "arguments[1]"); | |
} | |
f(passedValue1, passedValue2); | |
} | |
}, | |
test27: { | |
name: "formal arg: convert to ES5 version, change value and set writable to false", | |
body: function () { | |
var passedValue1 = 1; | |
var val = 2; | |
function f(a) { | |
Object.defineProperty(arguments, 0, { enumerable: true }); | |
a = val; | |
Object.defineProperty(arguments, 0, { writable: false }); | |
var expected = { configurable: true, enumerable: true, writable: false, value: val }; | |
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0)); | |
} | |
f(passedValue1); | |
} | |
}, | |
test28: { | |
name: "formal arg: convert to ES5 version, enumerate when number of actual params is less than number of formals", | |
body: function () { | |
var accumulator = ""; | |
function f(a, b) { | |
Object.preventExtensions(arguments); | |
for (var i in arguments) { | |
if (accumulator.length != 0) accumulator += ","; | |
accumulator += arguments[i]; | |
} | |
} | |
var value = 5; | |
f(value); | |
var expected = helpers.isVersion10OrLater ? | |
value.toString() : | |
value.toString() + ",undefined"; // IE9 compat mode -- Win8 558490. | |
assert.areEqual(expected, accumulator, "Wrong accumulated value"); | |
} | |
}, | |
} // tests. | |
testRunner.runTests(tests); |