//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// JavaScript source code
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

var handler = {
    getOwnPropertyDescriptor: function (target, name) {
        return emulatedProps[name];
    },
    defineProperty: function (target, name, desc) {

        emulatedProps[name] = desc;
        // $LOG('success: ' + success);
        return success[name];
    },
    preventExtensions: function (target) {
        Object.defineProperties(target, emulatedProps);
        Object.preventExtensions(target);
        return true;
    },
    deleteProperty: function (target, name) {
        delete emulatedProps[name];
        return success[name];
    },
    get: function (target, name, receiver) {
        var desc = emulatedProps[name];
        if (desc === undefined) { return emulatedProto[name]; }
        if ('value' in desc) return desc.value;
        if ('get' in desc) return desc.get.call(target);
    },
    set: function (target, name, value, receiver) {
        return success[name];
    },
    has: function (target, name) {
        return !!emulatedProps[name];
    },
    hasOwn: function (target, name) {
        return !!emulatedProps[name];
    },
    ownKeys: function (target) {
        return Object.getOwnPropertyNames(emulatedProps);
    },
    enumerate: function (target) {
        return Object.getOwnPropertyNames(emulatedProps).filter(function (name) {
            return emulatedProps[name].enumerable;
        });
    }
};

var tests = [
    {
        name: "non-configurable property can't be reported as non-existent",
        body: function () {
            var target = {};
            emulatedProps = {};
            emulatedProps.x = { value: 'test', configurable: false };

            Object.defineProperty(target, 'x', emulatedProps.x);

            var proxy = new Proxy(target, handler); // Checkout handler code at the bottom
            var desc;
            delete emulatedProps.x;

            assert.throws(function () { desc = Object.getOwnPropertyDescriptor(proxy, 'x'); }, TypeError, "Cannot return non-existent for a non-configurable property");
        },
    },
    {
        name: "existing property on non-extensible object cannot be reported as non-existent",
        body: function(){
            var target = {};
            emulatedProps = {};

            target.x = 20;
            Object.preventExtensions(target);
            var desc;
            var proxy = new Proxy(target, handler); // Checkout handler code at the bottom

            assert.throws(function () { desc = Object.getOwnPropertyDescriptor(proxy, 'x'); }, TypeError);
        }
    },
    {
        name: "non existing property on non-extensible object cannot be reported as existent",
        body: function () {
            var target = {};
            emulatedProps = {};
            emulatedProps.x = { value: 1, configurable: false };
            Object.preventExtensions(target);
            var proxy = new Proxy(target, handler);
            assert.throws(function () { Object.getOwnPropertyDescriptor(proxy, 'x') }, TypeError);
        }
    },
    {
        name:'configurable property cannot be reported as non-configurable',
        body: function () {
            var target = {};
            emulatedProps = {};
            emulatedProps.x = { value: 1, configurable: false };
            Object.defineProperty(target, 'x', { value: 1, configurable: true });
            var proxy = new Proxy(target, handler);
            assert.throws(function () { desc = Object.getOwnPropertyDescriptor(proxy, 'x') }, TypeError, "configurable property cannot be reported as non-configurable");
        }
    },
    {
        name: "a non-existent property cannot be reported as non-configurable",
        body: function() {
            emulatedProps = {};
            var target = {};
            Object.defineProperty(emulatedProps, 'y', { value: 'test', configurable: 'false' });
            var proxy = new Proxy(target, handler);
            assert.throws(function () { var desc = Object.getOwnPropertyDescriptor(proxy, 'y') }, TypeError, "a non-existent property cannot be reported as non-configurable");
        }
    },
    {
        name: "can't add property on non-extensible object.",
        body: function () {
            var target = {};
            Object.preventExtensions(target);
            emulatedProps = {};
            emulatedProps.x = 5;
            var proxy = new Proxy(target, emulatedProps);
            success = function () { };
            assert.throws(function () { Object.defineProperty(proxy, 'x', { value: 20 }) }, TypeError);
        },
    },
    {
        name: "target property must be non-configurable after set as such in proxy #1",
        body: function () {
            var target = {};
            emulatedProps = {};
            var proxy = new Proxy(target, handler);
            success = function () { };
            success.x = 21;
            assert.throws(function () { Object.defineProperty(proxy, 'x', { value: 20, configurable: false }) }, TypeError);
        },
    },
    {
        name: "target property must be non-configurable after set as such in proxy #2",
        body: function () {
            var target = {};
            emulatedProps = {};
            emulatedProps.x = 5;
            var proxy = new Proxy(target, handler);
            Object.defineProperty(target, 'x', { value: 41, configurable: true });
            success = function () { };
            success.x = 21;

            assert.throws(function () { Object.defineProperty(proxy, 'x', { value: 20, configurable: false }) }, TypeError);
        },
    },

];

testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });


