Files
Nicolas Chevobbe 4f13eaf338 Bug 2004579 - [devtools] Compute string index from character length in analyzeInputString. r=devtools-reviewers,bomsy.
We were only increasing the index by 1, which wouldn't account for surrogate pairs (e.g. emojis).
We need to increase the index by the character length so the string can be manipulated accurately
after.
Test case is added to cover the fix.

Differential Revision: https://phabricator.services.mozilla.com/D275419
2025-12-09 08:39:36 +00:00

238 lines
6.3 KiB
JavaScript

// 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`);
}
}
});