// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chai';
import * as FormatterWorker from './formatter_worker.js';
const mapping = new Map([
    ['varX', 'x'],
    ['varY', 'y'],
    ['varZ', 'z'],
    ['this', 'this_1'],
    ['rename_to_this', 'this'],
    ['rename_to_arguments', 'arguments'],
    ['varQ', null],
]);
function substitute(expression) {
    return FormatterWorker.Substitute.substituteExpression(expression, mapping);
}
describe('Substitute', () => {
    it('Preserves unrelated variable', () => {
        assert.strictEqual(substitute('x'), 'x');
    });
    it('Preserves `import.meta`', () => {
        assert.strictEqual(substitute('import.meta'), 'import.meta');
    });
    it('Substitutes single variable', () => {
        assert.strictEqual(substitute('varY'), 'y');
    });
    it('Substitutes await', () => {
        assert.strictEqual(substitute('await varX'), 'await x');
    });
    it('Substitutes array elements', () => {
        assert.strictEqual(substitute('[0, varY, varZ]'), '[0, y, z]');
        assert.strictEqual(substitute('[0, varY, ...varZ]'), '[0, y, ...z]');
    });
    it('Substitutes binary expressions, assignments, sequences', () => {
        assert.strictEqual(substitute('varX + varY'), 'x + y');
        assert.strictEqual(substitute('varX, varY'), 'x, y');
        assert.strictEqual(substitute('varX = varZ'), 'x = z');
        assert.strictEqual(substitute('varX += varZ'), 'x += z');
    });
    it('Substitutes arrow functions', () => {
        assert.strictEqual(substitute('(a) => a + varX'), '(a) => a + x');
        assert.strictEqual(substitute('(varX) => a + varX'), '(varX) => a + varX');
        // Capture avoiding substitution.
        assert.strictEqual(substitute('(x) => x + varX'), '(x_1) => x_1 + x');
        assert.strictEqual(substitute('(x) => (x) => x + varX'), '(x_2) => (x_1) => x_1 + x');
        assert.strictEqual(substitute('(x) => (x_1) => x + varX'), '(x_2) => (x_1) => x_2 + x');
    });
    it('Substitutes in blocks', () => {
        assert.strictEqual(substitute('{console.log(varY)}'), '{console.log(y)}');
        assert.strictEqual(substitute('{var y; console.log(varY + y)}'), '{var y_1; console.log(y + y_1)}');
        // Technically it would be correct to rename all instances of y to y_1,
        // but let us check we treat the let variables independently.
        assert.strictEqual(substitute('var y; {let y; console.log(varY + y)}; y'), 'var y_2; {let y_1; console.log(y + y_1)}; y_2');
        // Var hoisting out of blocks.
        assert.strictEqual(substitute('{var y; console.log(varY + y)}; y'), '{var y_1; console.log(y + y_1)}; y_1');
        assert.strictEqual(substitute('{let varY; console.log(varX + varY)}; varY'), '{let varY; console.log(x + varY)}; y');
        assert.strictEqual(substitute('{const varY = 1; console.log(varX + varY)}; varY'), '{const varY = 1; console.log(x + varY)}; y');
        assert.strictEqual(substitute('{var varY; console.log(varX + varY)}; varY'), '{var varY; console.log(x + varY)}; varY');
    });
    it('Substitutes in member expressions', () => {
        assert.strictEqual(substitute('varY.varX?.varZ'), 'y.varX?.varZ');
    });
    it('Substitutes in conditionals', () => {
        assert.strictEqual(substitute('varX ? varY : varZ'), 'x ? y : z');
        assert.strictEqual(substitute('if (varX) varY; else varZ;'), 'if (x) y; else z;');
    });
    it('Substitutes in loops', () => {
        assert.strictEqual(substitute('do { console.log(varX) } while (varY);'), 'do { console.log(x) } while (y);');
        assert.strictEqual(substitute('for (varX in varY) console.log(varZ);'), 'for (x in y) console.log(z);');
        assert.strictEqual(substitute('for (let varX in varY) console.log(varZ + varX);'), 'for (let varX in y) console.log(z + varX);');
        assert.strictEqual(substitute('for (let varX of varY) console.log(varZ + varX);'), 'for (let varX of y) console.log(z + varX);');
        assert.strictEqual(substitute('for (let x of varY) console.log(x + varX);'), 'for (let x_1 of y) console.log(x_1 + x);');
        assert.strictEqual(substitute('for (varX = 1; varX < 100; varX++) console.log(varX);'), 'for (x = 1; x < 100; x++) console.log(x);');
        assert.strictEqual(substitute('for (var varX = 1; varX < 100; varX++) console.log(varX);'), 'for (var varX = 1; varX < 100; varX++) console.log(varX);');
        assert.strictEqual(substitute('while (varX) { varY; }'), 'while (x) { y; }');
    });
    it('Substitutes in functions', () => {
        assert.strictEqual(substitute('function f(a) { a + varX; }'), 'function f(a) { a + x; }');
        assert.strictEqual(substitute('function f(varX) { a + varX; }'), 'function f(varX) { a + varX; }');
        // Capture avoiding substitution.
        assert.strictEqual(substitute('function f(x) { return (x_1) => x + varX; }'), 'function f(x_2) { return (x_1) => x_2 + x; }');
        assert.strictEqual(substitute('function f(...varX) { varX[0] }'), 'function f(...varX) { varX[0] }');
        assert.strictEqual(substitute('function f(...x) { varX[0] }'), 'function f(...x_1) { x[0] }');
        assert.strictEqual(substitute('function f() { this; }; this'), 'function f() { this; }; this_1');
    });
    it('Substitutes in object literals', () => {
        assert.strictEqual(substitute('{ varX: varX }'), '{ varX: x }');
        assert.strictEqual(substitute('({ [varY]: varX })'), '({ [y]: x })');
        assert.strictEqual(substitute('({ ...varX })'), '({ ...x })');
        assert.strictEqual(substitute('({ varX })'), '({ varX: x })');
    });
    it('Substitutes in switch', () => {
        assert.strictEqual(substitute('switch (varX) { case varY: varZ; }'), 'switch (x) { case y: z; }');
        assert.strictEqual(substitute('switch (varY) { case varZ: varX; case 2: let x = 1; }'), 'switch (y) { case z: x; case 2: let x_1 = 1; }');
    });
    it('Substitutes in generators', () => {
        assert.strictEqual(substitute('function* f(x) { yield varX + x; }'), 'function* f(x_1) { yield x + x_1; }');
    });
    it('Substitutes in template literals', () => {
        assert.strictEqual(substitute('`${varX}`'), '`${x}`');
        assert.strictEqual(substitute('varY`${varX} varZ`'), 'y`${x} varZ`');
    });
    it('Substitutes in patterns', () => {
        assert.strictEqual(substitute('[varX, varY] = [varZ, 1]'), '[x, y] = [z, 1]');
        assert.strictEqual(substitute('[varX[varY]] = [3]'), '[x[y]] = [3]');
        assert.strictEqual(substitute('[varX.varY] = [3]'), '[x.varY] = [3]');
        assert.strictEqual(substitute('({varX: varZ} = {varX: 3})'), '({varX: z} = {varX: 3})');
        assert.strictEqual(substitute('[varX = varY] = []'), '[x = y] = []');
        assert.strictEqual(substitute('({varX: varZ.x} = {varX: 3})'), '({varX: z.x} = {varX: 3})');
        assert.strictEqual(substitute('let [varX, varY] = [varZ, 1]; varX'), 'let [varX, varY] = [z, 1]; varX');
        assert.strictEqual(substitute('let [varX = varY] = []'), 'let [varX = y] = []');
        assert.strictEqual(substitute('var {varX: varZ} = {varX: 3}; varZ'), 'var {varX: varZ} = {varX: 3}; varZ');
        // Avoid captures.
        assert.strictEqual(substitute('let [y, x] = [varX, varY]; x'), 'let [y_1, x_1] = [x, y]; x_1');
        assert.strictEqual(substitute('var {varX: x} = {varX}; x'), 'var {varX: x_1} = {varX: x}; x_1');
    });
    it('Does not substitute in strings', () => {
        assert.strictEqual(substitute('\'varX\''), '\'varX\'');
        assert.strictEqual(substitute('\"varX\"'), '\"varX\"');
        assert.strictEqual(substitute('\`varX\`'), '\`varX\`');
    });
    it('Does not substitute in comments', () => {
        assert.strictEqual(substitute('/* varX */'), '/* varX */');
        assert.strictEqual(substitute('// varX'), '// varX');
    });
    it('Throws on to-be-captured "this" and "arguments" bound by a function', () => {
        assert.throws(() => substitute('function f() { return rename_to_this; }'), 'Cannot avoid capture of \'this\'');
        assert.throws(() => substitute('function f() { return rename_to_arguments; }'), 'Cannot avoid capture of \'arguments\'');
    });
    it('Throws on parse error', () => {
        assert.throws(() => substitute('('), SyntaxError);
    });
    it('Throws if the renamed variable is unavailable', () => {
        assert.throws(() => substitute('varQ'), Error);
    });
});
//# sourceMappingURL=Substitute.test.js.map