/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */

// Test cases for MessageTray URLification

const JsUnit = imports.jsUnit;

import '../../js/ui/environment.js';

import * as Assertions from '../common/assertions.js';

const JsParse = imports.misc.jsParse;

const HARNESS_COMMAND_HEADER = 'let imports = obj;' +
                               'let global = obj;' +
                               'let Main = obj;' +
                               'let foo = obj;' +
                               'let r = obj;';

const testsFindMatchingQuote = [
    {
        input: '"double quotes"',
        output: 0,
    },
    {
        input: '\'single quotes\'',
        output: 0,
    },
    {
        input: 'some unquoted "some quoted"',
        output: 14,
    },
    {
        input: '"mixed \' quotes\'"',
        output: 0,
    },
    {
        input: '"escaped \\" quote"',
        output: 0,
    },
];
const testsFindMatchingSlash = [
    {
        input: '/slash/',
        output: 0,
    },
    {
        input: '/slash " with $ funny ^\' stuff/',
        output: 0,
    },
    {
        input: 'some unslashed /some slashed/',
        output: 15,
    },
    {
        input: '/escaped \\/ slash/',
        output: 0,
    },
];
const testsFindMatchingBrace = [
    {
        input: '[square brace]',
        output: 0,
    },
    {
        input: '(round brace)',
        output: 0,
    },
    {
        input: '([()][nesting!])',
        output: 0,
    },
    {
        input: '[we have "quoted [" braces]',
        output: 0,
    },
    {
        input: '[we have /regex [/ braces]',
        output: 0,
    },
    {
        input: '([[])[] mismatched braces ]',
        output: 1,
    },
];
const testsGetExpressionOffset = [
    {
        input: 'abc.123',
        output: 0,
    },
    {
        input: 'foo().bar',
        output: 0,
    },
    {
        input: 'foo(bar',
        output: 4,
    },
    {
        input: 'foo[abc.match(/"/)]',
        output: 0,
    },
];
const testsGetDeclaredConstants = [
    {
        input: 'const foo = X; const bar = Y;',
        output: ['foo', 'bar'],
    },
    {
        input: 'const foo=X; const bar=Y',
        output: ['foo', 'bar'],
    },
];
const testsIsUnsafeExpression = [
    {
        input: 'foo.bar',
        output: false,
    },
    {
        input: 'foo[\'bar\']',
        output: false,
    },
    {
        input: 'foo["a=b=c".match(/=/)',
        output: false,
    },
    {
        input: 'foo[1==2]',
        output: false,
    },
    {
        input: '(x=4)',
        output: true,
    },
    {
        input: '(x = 4)',
        output: true,
    },
    {
        input: '(x;y)',
        output: true,
    },
];
const testsModifyScope = [
    "foo['a",
    "foo()['b'",
    "obj.foo()('a', 1, 2, 'b')().",
    'foo.[.',
    'foo]]]()))].',
    "123'ab\"",
    'Main.foo.bar = 3; bar.',
    '(Main.foo = 3).',
    'Main[Main.foo+=-1].',
];

//
// Test javascript parsing
//

for (let i = 0; i < testsFindMatchingQuote.length; i++) {
    let text = testsFindMatchingQuote[i].input;
    let match = JsParse.findMatchingQuote(text, text.length - 1);

    JsUnit.assertEquals(`Test testsFindMatchingQuote ${i}`,
        match, testsFindMatchingQuote[i].output);
}

for (let i = 0; i < testsFindMatchingSlash.length; i++) {
    let text = testsFindMatchingSlash[i].input;
    let match = JsParse.findMatchingSlash(text, text.length - 1);

    JsUnit.assertEquals(`Test testsFindMatchingSlash ${i}`,
        match, testsFindMatchingSlash[i].output);
}

for (let i = 0; i < testsFindMatchingBrace.length; i++) {
    let text = testsFindMatchingBrace[i].input;
    let match = JsParse.findMatchingBrace(text, text.length - 1);

    JsUnit.assertEquals(`Test testsFindMatchingBrace ${i}`,
        match, testsFindMatchingBrace[i].output);
}

for (let i = 0; i < testsGetExpressionOffset.length; i++) {
    let text = testsGetExpressionOffset[i].input;
    let match = JsParse.getExpressionOffset(text, text.length - 1);

    JsUnit.assertEquals(`Test testsGetExpressionOffset ${i}`,
        match, testsGetExpressionOffset[i].output);
}

for (let i = 0; i < testsGetDeclaredConstants.length; i++) {
    let text = testsGetDeclaredConstants[i].input;
    let match = JsParse.getDeclaredConstants(text);

    Assertions.assertArrayEquals(`Test testsGetDeclaredConstants ${i}`,
        match, testsGetDeclaredConstants[i].output);
}

for (let i = 0; i < testsIsUnsafeExpression.length; i++) {
    let text = testsIsUnsafeExpression[i].input;
    let unsafe = JsParse.isUnsafeExpression(text);

    JsUnit.assertEquals(`Test testsIsUnsafeExpression ${i}`,
        unsafe, testsIsUnsafeExpression[i].output);
}

//
// Test safety of eval to get completions
//

for (let i = 0; i < testsModifyScope.length; i++) {
    let text = testsModifyScope[i];
    // We need to use var here for the with statement
    var obj = {};

    // Just as in JsParse.getCompletions, we will find the offset
    // of the expression, test whether it is unsafe, and then eval it.
    let offset = JsParse.getExpressionOffset(text, text.length - 1);
    if (offset >= 0) {
        text = text.slice(offset);

        let matches = text.match(/(.*)\.(.*)/);
        if (matches) {
            let [, base] = matches;

            if (!JsParse.isUnsafeExpression(base)) {
                try {
                    eval(HARNESS_COMMAND_HEADER + base);
                } catch (e) {
                    JsUnit.assertNotEquals(`Code '${base}' is valid code`, e.constructor, SyntaxError);
                }
            }
        }
    }
    let propertyNames = Object.getOwnPropertyNames(obj);
    JsUnit.assertEquals(`The context '${JSON.stringify(obj)}' was not modified`, propertyNames.length, 0);
}