// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

"use strict";
const {
  analyzeInputString,
} = require("resource://devtools/shared/webconsole/analyze-input-string.js");

add_task(() => {
  const tests = [
    {
      desc: "simple property access",
      input: `var a = {b: 1};a.b`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `var a = {b: 1};a`,
        lastStatement: "a.b",
        mainExpression: `a`,
        matchProp: `b`,
      },
    },
    {
      desc: "deep property access",
      input: `a.b.c`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a.b`,
        lastStatement: "a.b.c",
        mainExpression: `a.b`,
        matchProp: `c`,
      },
    },
    {
      desc: "element access",
      input: `a["b`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a`,
        lastStatement: `a["b`,
        mainExpression: `a`,
        matchProp: `"b`,
      },
    },
    {
      desc: "element access without quotes",
      input: `a[b`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a`,
        lastStatement: `a[b`,
        mainExpression: `a`,
        matchProp: `b`,
      },
    },
    {
      desc: "simple optional chaining access",
      input: `a?.b`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a`,
        lastStatement: `a?.b`,
        mainExpression: `a`,
        matchProp: `b`,
      },
    },
    {
      desc: "deep optional chaining access",
      input: `a?.b?.c`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a?.b`,
        lastStatement: `a?.b?.c`,
        mainExpression: `a?.b`,
        matchProp: `c`,
      },
    },
    {
      desc: "optional chaining element access",
      input: `a?.["b`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a`,
        lastStatement: `a?.["b`,
        mainExpression: `a`,
        matchProp: `"b`,
      },
    },
    {
      desc: "optional chaining element access without quotes",
      input: `a?.[b`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `a`,
        lastStatement: `a?.[b`,
        mainExpression: `a`,
        matchProp: `b`,
      },
    },
    {
      desc: "deep optional chaining element access with quotes",
      input: `var a = {b: 1, c: ["."]};     a?.["b"]?.c?.["d[.`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `var a = {b: 1, c: ["."]};     a?.["b"]?.c`,
        lastStatement: `a?.["b"]?.c?.["d[.`,
        mainExpression: `a?.["b"]?.c`,
        matchProp: `"d[.`,
      },
    },
    {
      desc: "literal arrays with newline",
      input: `[1,2,3,\n4\n].`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `[1,2,3,\n4\n]`,
        lastStatement: `[1,2,3,4].`,
        mainExpression: `[1,2,3,4]`,
        matchProp: ``,
      },
    },
    {
      desc: "number literal with newline",
      input: `1\n.`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `1\n`,
        lastStatement: `1\n.`,
        mainExpression: `1`,
        matchProp: ``,
      },
    },
    {
      desc: "string literal",
      input: `"abc".`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `"abc"`,
        lastStatement: `"abc".`,
        mainExpression: `"abc"`,
        matchProp: ``,
      },
    },
    {
      desc: "string literal containing backslash",
      input: `"\\n".`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `"\\n"`,
        lastStatement: `"\\n".`,
        mainExpression: `"\\n"`,
        matchProp: ``,
      },
    },
    {
      desc: "single quote string literal containing backslash",
      input: `'\\r'.`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `'\\r'`,
        lastStatement: `'\\r'.`,
        mainExpression: `'\\r'`,
        matchProp: ``,
      },
    },
    {
      desc: "template string literal containing backslash",
      input: "`\\\\`.",
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: "`\\\\`",
        lastStatement: "`\\\\`.",
        mainExpression: "`\\\\`",
        matchProp: ``,
      },
    },
    {
      desc: "unterminated double quote string literal",
      input: `"\n`,
      expected: {
        err: "unterminated string literal",
      },
    },
    {
      desc: "unterminated single quote string literal",
      input: `'\n`,
      expected: {
        err: "unterminated string literal",
      },
    },
    {
      desc: "string literal with surrogate pair character",
      input: `"🧑".c`,
      expected: {
        isElementAccess: false,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `"🧑"`,
        lastStatement: `"🧑".c`,
        mainExpression: `"🧑"`,
        matchProp: `c`,
      },
    },
    {
      desc: "optional chaining operator with spaces",
      input: `test  ?.    ["propA"]  ?.   [0]  ?.   ["propB"]  ?.  ['to`,
      expected: {
        isElementAccess: true,
        isPropertyAccess: true,
        expressionBeforePropertyAccess: `test  ?.    ["propA"]  ?.   [0]  ?.   ["propB"]  `,
        lastStatement: `test  ?.    ["propA"]  ?.   [0]  ?.   ["propB"]  ?.  ['to`,
        mainExpression: `test  ?.    ["propA"]  ?.   [0]  ?.   ["propB"]`,
        matchProp: `'to`,
      },
    },
  ];

  for (const { input, desc, expected } of tests) {
    const result = analyzeInputString(input);
    for (const [key, value] of Object.entries(expected)) {
      Assert.equal(value, result[key], `${desc} | ${key} has expected value`);
    }
  }
});
