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

WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

let invalidScripts = [
    [" #!\n", "Hashbang must be the first token (even before whitespace)"],
    ["\n#!\n", "Hashbang must be the first token (even before whitespace)"],
    ["##!\n", "Hashbang token is '#!'"],
    [";#!\n", "Hashbang must be the first token"],
    ["'use strict'#!\n", "Hashbang must come before 'use strict'"],
    ["#!\n#!\n", "Only one hashbang may exist because it has to be the first token"],
    ["function foo() {\n#!\n}", "Hashbang must be the first token in the script (not local function)"],
    ["function foo() {#!\n}", "Hashbang must be the first token in the script (not local function)"],
    ["#\\041\n", "Hashbang can't be made of encoded characters"],
    ["#\\u0021\n", "Hashbang can't be made of encoded characters"],
    ["#\\u{21}\n", "Hashbang can't be made of encoded characters"],
    ["#\\x21\n", "Hashbang can't be made of encoded characters"],
    ["\\043!\n", "Hashbang can't be made of encoded characters"],
    ["\\u0023!\n", "Hashbang can't be made of encoded characters"],
    ["\\u{23}!\n", "Hashbang can't be made of encoded characters"],
    ["\\x23!\n", "Hashbang can't be made of encoded characters"],
    ["\\u0023\\u0021\n", "Hashbang can't be made of encoded characters"],
    ["Function('#!\n','')", "Hashbang is not valid in function evaluator contexts"],
    ["new Function('#!\n','')", "Hashbang is not valid in function evaluator contexts"],
    ["{\n#!\n}\n", "Hashbang not valid in block"],
    ["#!/*\nthrow 123;\n*/\nthrow 456;", "Hashbang comments out a single line"],
    ["\\\\ single line comment\n#! hashbang\n", "Single line comment may not preceed hashbang"],
    ["/**/#! hashbang\n", "Multi-line comment may not preceed hashbang"],
    ["/**/\n#! hashbang\n", "Multi-line comment may not preceed hashbang"],
];

var tests = [
    {
        name: "Valid hashbang in ordinary script",
        body: function () {
            assert.areEqual(2, WScript.LoadScript("#! throw 'error';\nthis.prop=2;").prop);
            assert.areEqual(3, WScript.LoadScript("#! throw 'error'\u{000D}this.prop=3;").prop);
            assert.areEqual(4, WScript.LoadScript("#! throw 'error'\u{2028}this.prop=4;").prop);
            assert.areEqual(5, WScript.LoadScript("#! throw 'error'\u{2029}this.prop=5;").prop);
        }
    },
    {
        name: "Valid hashbang in module script",
        body: function () {
            WScript.RegisterModuleSource('module_hashbang_valid.js', "#! export default 123;\n export default 456;");
            
            testRunner.LoadModule(`
                import {default as prop} from 'module_hashbang_valid.js';
                assert.areEqual(456, prop);
            `, 'samethread', false, false);
        }
    },
    {
        name: "Valid hashbang in eval",
        body: function () {
            assert.areEqual(undefined, eval('#!'));
            assert.areEqual(undefined, eval('#!\n'));
            assert.areEqual(1, eval('#!\n1'));
            assert.areEqual(undefined, eval('#!2\n'));
        }
    },
    {
        name: "Valid hashbang in indirect eval",
        body: function () {
            let _eval = eval;
            assert.areEqual(undefined, _eval('#!'));
            assert.areEqual(undefined, _eval('#!\n'));
            assert.areEqual(1, _eval('#!\n1'));
            assert.areEqual(undefined, _eval('#!2\n'));
        }
    },
    {
        name: "Invalid hashbang in ordinary script",
        body: function () {
            for (a of invalidScripts) {
                assert.throws(()=>WScript.LoadScript(a[0]), SyntaxError, a[1]);
            }
        }
    },
    {
        name: "Invalid hashbang in module script",
        body: function () {
            for (a of invalidScripts) {
                assert.throws(()=>WScript.LoadModule(a[0]), SyntaxError, a[1]);
            }
        }
    },
    {
        name: "Invalid hashbang in eval",
        body: function () {
            for (a of invalidScripts) {
                assert.throws(()=>eval(a[0]), SyntaxError, a[1]);
            }
        }
    },
    {
        name: "Invalid hashbang in indirect eval",
        body: function () {
            let _eval = eval;
            for (a of invalidScripts) {
                assert.throws(()=>_eval(a[0]), SyntaxError, a[1]);
            }
        }
    },
];

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