summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js')
-rw-r--r--devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js816
1 files changed, 816 insertions, 0 deletions
diff --git a/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js b/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js
new file mode 100644
index 0000000000..228d2dc79d
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js
@@ -0,0 +1,816 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const RuleRewriter = require("resource://devtools/client/fronts/inspector/rule-rewriter.js");
+const {
+ isCssPropertyKnown,
+} = require("resource://devtools/server/actors/css-properties.js");
+
+const TEST_DATA = [
+ {
+ desc: "simple set",
+ input: "p:v;",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
+ expected: "p:N;",
+ },
+ {
+ desc: "simple set clearing !important",
+ input: "p:v !important;",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
+ expected: "p:N;",
+ },
+ {
+ desc: "simple set adding !important",
+ input: "p:v;",
+ instruction: {
+ type: "set",
+ name: "p",
+ value: "N",
+ priority: "important",
+ index: 0,
+ },
+ expected: "p:N !important;",
+ },
+ {
+ desc: "simple set between comments",
+ input: "/*color:red;*/ p:v; /*color:green;*/",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 1 },
+ expected: "/*color:red;*/ p:N; /*color:green;*/",
+ },
+ // The rule view can generate a "set" with a previously unknown
+ // property index; which should work like "create".
+ {
+ desc: "set at unknown index",
+ input: "a:b; e: f;",
+ instruction: { type: "set", name: "c", value: "d", priority: "", index: 2 },
+ expected: "a:b; e: f;c: d;",
+ },
+ {
+ desc: "simple rename",
+ input: "p:v;",
+ instruction: { type: "rename", name: "p", newName: "q", index: 0 },
+ expected: "q:v;",
+ },
+ // "rename" is passed the name that the user entered, and must do
+ // any escaping necessary to ensure that this is an identifier.
+ {
+ desc: "rename requiring escape",
+ input: "p:v;",
+ instruction: { type: "rename", name: "p", newName: "a b", index: 0 },
+ expected: "a\\ b:v;",
+ },
+ {
+ desc: "simple create",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "v",
+ priority: "important",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: v !important;",
+ },
+ {
+ desc: "create between two properties",
+ input: "a:b; e: f;",
+ instruction: {
+ type: "create",
+ name: "c",
+ value: "d",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "a:b; c: d;e: f;",
+ },
+ // "create" is passed the name that the user entered, and must do
+ // any escaping necessary to ensure that this is an identifier.
+ {
+ desc: "create requiring escape",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "a b",
+ value: "d",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "a\\ b: d;",
+ },
+ {
+ desc: "simple disable",
+ input: "p:v;",
+ instruction: { type: "enable", name: "p", value: false, index: 0 },
+ expected: "/*! p:v; */",
+ },
+ {
+ desc: "simple enable",
+ input: "/* color:v; */",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:v;",
+ },
+ {
+ desc: "enable with following property in comment",
+ input: "/* color:red; color: blue; */",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:red; /* color: blue; */",
+ },
+ {
+ desc: "enable with preceding property in comment",
+ input: "/* color:red; color: blue; */",
+ instruction: { type: "enable", name: "color", value: true, index: 1 },
+ expected: "/* color:red; */ color: blue;",
+ },
+ {
+ desc: "simple remove",
+ input: "a:b;c:d;e:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "a:b;e:f;",
+ },
+ {
+ desc: "disable with comment ender in string",
+ input: "content: '*/';",
+ instruction: { type: "enable", name: "content", value: false, index: 0 },
+ expected: "/*! content: '*\\/'; */",
+ },
+ {
+ desc: "enable with comment ender in string",
+ input: "/* content: '*\\/'; */",
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: "content: '*/';",
+ },
+ {
+ desc: "enable requiring semicolon insertion",
+ // Note the lack of a trailing semicolon in the comment.
+ input: "/* color:red */ color: blue;",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:red; color: blue;",
+ },
+ {
+ desc: "create requiring semicolon insertion",
+ // Note the lack of a trailing semicolon.
+ input: "color: red",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "color: red;a: b;",
+ },
+
+ // Newline insertion.
+ {
+ desc: "simple newline insertion",
+ input: "\ncolor: red;\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+ // Newline insertion.
+ {
+ desc: "semicolon insertion before newline",
+ // Note the lack of a trailing semicolon.
+ input: "\ncolor: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+ // Newline insertion.
+ {
+ desc: "newline and semicolon insertion",
+ // Note the lack of a trailing semicolon and newline.
+ input: "\ncolor: red",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+
+ // Newline insertion and indentation.
+ {
+ desc: "indentation with create",
+ input: "\n color: red;\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n",
+ },
+ // Newline insertion and indentation.
+ {
+ desc: "indentation plus semicolon insertion before newline",
+ // Note the lack of a trailing semicolon.
+ input: "\n color: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n",
+ },
+ {
+ desc: "indentation inserted before trailing whitespace",
+ // Note the trailing whitespace. This could come from a rule
+ // like:
+ // @supports (mumble) {
+ // body {
+ // color: red;
+ // }
+ // }
+ // Here if we create a rule we don't want it to follow
+ // the indentation of the "}".
+ input: "\n color: red;\n ",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n ",
+ },
+ // Newline insertion and indentation.
+ {
+ desc: "indentation comes from preceding comment",
+ // Note how the comment comes before the declaration.
+ input: "\n /* comment */ color: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n /* comment */ color: red;\n a: b;\n",
+ },
+ // Default indentation.
+ {
+ desc: "use of default indentation",
+ input: "\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "\n\ta: b;\n",
+ },
+
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion removes newline",
+ input: "a:b;\nc:d;\ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "a:b;\ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion remove blank line",
+ input: "\n a:b;\n c:d; \ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion leaves comment",
+ input: "\n a:b;\n /* something */ c:d; \ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\n /* something */ \ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion leaves previous newline",
+ input: "\n a:b;\n c:d; \ne:f;",
+ instruction: { type: "remove", name: "e", index: 2 },
+ expected: "\n a:b;\n c:d; \n",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion removes trailing whitespace",
+ input: "\n a:b;\n c:d; \n e:f;",
+ instruction: { type: "remove", name: "e", index: 2 },
+ expected: "\n a:b;\n c:d; \n",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion preserves indentation",
+ input: " a:b;\n c:d; \n e:f;",
+ instruction: { type: "remove", name: "a", index: 0 },
+ expected: " c:d; \n e:f;",
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable single quote termination",
+ input: "/* content: 'hi */ color: red;",
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: "content: 'hi'; color: red;",
+ changed: { 0: "'hi'" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create single quote termination",
+ input: "content: 'hi",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "content: 'hi';color: red;",
+ changed: { 0: "'hi'" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable double quote termination",
+ input: '/* content: "hi */ color: red;',
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: 'content: "hi"; color: red;',
+ changed: { 0: '"hi"' },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create double quote termination",
+ input: 'content: "hi',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'content: "hi";color: red;',
+ changed: { 0: '"hi"' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable url termination",
+ input: "/* background-image: url(something.jpg */ color: red;",
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: "background-image: url(something.jpg); color: red;",
+ changed: { 0: "url(something.jpg)" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create url termination",
+ input: "background-image: url(something.jpg",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "background-image: url(something.jpg);color: red;",
+ changed: { 0: "url(something.jpg)" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable url single quote termination",
+ input: "/* background-image: url('something.jpg */ color: red;",
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: "background-image: url('something.jpg'); color: red;",
+ changed: { 0: "url('something.jpg')" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create url single quote termination",
+ input: "background-image: url('something.jpg",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "background-image: url('something.jpg');color: red;",
+ changed: { 0: "url('something.jpg')" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "create url double quote termination",
+ input: '/* background-image: url("something.jpg */ color: red;',
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: 'background-image: url("something.jpg"); color: red;',
+ changed: { 0: 'url("something.jpg")' },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "enable url double quote termination",
+ input: 'background-image: url("something.jpg',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'background-image: url("something.jpg");color: red;',
+ changed: { 0: 'url("something.jpg")' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "create backslash termination",
+ input: "something: \\",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: \\\\;color: red;",
+ // The lexer rewrites the token before we see it. However this is
+ // so obscure as to be inconsequential.
+ changed: { 0: "\uFFFD\\" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable backslash single quote termination",
+ input: "something: '\\",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: '\\\\';color: red;",
+ changed: { 0: "'\\\\'" },
+ },
+ {
+ desc: "enable backslash double quote termination",
+ input: 'something: "\\',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'something: "\\\\";color: red;',
+ changed: { 0: '"\\\\"' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable comment termination",
+ input: "something: blah /* comment ",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: blah /* comment*/; color: red;",
+ },
+
+ // Rewrite a "heuristic override" comment.
+ {
+ desc: "enable with heuristic override comment",
+ input: "/*! walrus: zebra; */",
+ instruction: { type: "enable", name: "walrus", value: true, index: 0 },
+ expected: "walrus: zebra;",
+ },
+
+ // Sanitize a bad value.
+ {
+ desc: "create sanitize unpaired brace",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "}",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: \\};",
+ changed: { 0: "\\}" },
+ },
+ // Sanitize a bad value.
+ {
+ desc: "set sanitize unpaired brace",
+ input: "walrus: zebra;",
+ instruction: {
+ type: "set",
+ name: "walrus",
+ value: "{{}}}",
+ priority: "",
+ index: 0,
+ },
+ expected: "walrus: {{}}\\};",
+ changed: { 0: "{{}}\\}" },
+ },
+ // Sanitize a bad value.
+ {
+ desc: "enable sanitize unpaired brace",
+ input: "/*! walrus: }*/",
+ instruction: { type: "enable", name: "walrus", value: true, index: 0 },
+ expected: "walrus: \\};",
+ changed: { 0: "\\}" },
+ },
+
+ // Creating a new declaration does not require an attempt to
+ // terminate a previous commented declaration.
+ {
+ desc: "disabled declaration does not need semicolon insertion",
+ input: "/*! no: semicolon */\n",
+ instruction: {
+ type: "create",
+ name: "walrus",
+ value: "zebra",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "/*! no: semicolon */\nwalrus: zebra;\n",
+ changed: {},
+ },
+
+ {
+ desc: "create commented-out property",
+ input: "p: v",
+ instruction: {
+ type: "create",
+ name: "shoveler",
+ value: "duck",
+ priority: "",
+ index: 1,
+ enabled: false,
+ },
+ expected: "p: v;/*! shoveler: duck; */",
+ },
+ {
+ desc: "disabled create with comment ender in string",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "content",
+ value: "'*/'",
+ priority: "",
+ index: 0,
+ enabled: false,
+ },
+ expected: "/*! content: '*\\/'; */",
+ },
+
+ {
+ desc: "delete disabled property",
+ input: "\n a:b;\n /* color:#f06; */\n e:f;",
+ instruction: { type: "remove", name: "color", index: 1 },
+ expected: "\n a:b;\n e:f;",
+ },
+ {
+ desc: "delete heuristic-disabled property",
+ input: "\n a:b;\n /*! c:d; */\n e:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\n e:f;",
+ },
+ {
+ desc: "delete disabled property leaving other disabled property",
+ input: "\n a:b;\n /* color:#f06; background-color: seagreen; */\n e:f;",
+ instruction: { type: "remove", name: "color", index: 1 },
+ expected: "\n a:b;\n /* background-color: seagreen; */\n e:f;",
+ },
+
+ {
+ desc: "regression test for bug 1328016",
+ input: "position:absolute;top50px;height:50px;width:50px;",
+ instruction: {
+ type: "set",
+ name: "width",
+ value: "60px",
+ priority: "",
+ index: 2,
+ },
+ expected: "position:absolute;top50px;height:50px;width:60px;",
+ },
+
+ {
+ desc: "url regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "url(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: url();",
+ changed: { 0: "url()" },
+ },
+
+ {
+ desc: "url semicolon regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "url(;",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: url();",
+ changed: { 0: "url()" },
+ },
+
+ {
+ desc: "basic regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: \\(;",
+ changed: { 0: "\\(" },
+ },
+
+ {
+ desc: "unbalanced regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "({[})",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: ({\\[});",
+ changed: { 0: "({\\[})" },
+ },
+
+ {
+ desc: "function regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "func(1,2)",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: func(1,2);",
+ },
+
+ {
+ desc: "function regression test for bug 1355233",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "func(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: func\\(;",
+ changed: { 0: "func\\(" },
+ },
+];
+
+function rewriteDeclarations(inputString, instruction, defaultIndentation) {
+ const rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
+ rewriter.defaultIndentation = defaultIndentation;
+
+ switch (instruction.type) {
+ case "rename":
+ rewriter.renameProperty(
+ instruction.index,
+ instruction.name,
+ instruction.newName
+ );
+ break;
+
+ case "enable":
+ rewriter.setPropertyEnabled(
+ instruction.index,
+ instruction.name,
+ instruction.value
+ );
+ break;
+
+ case "create":
+ rewriter.createProperty(
+ instruction.index,
+ instruction.name,
+ instruction.value,
+ instruction.priority,
+ instruction.enabled
+ );
+ break;
+
+ case "set":
+ rewriter.setProperty(
+ instruction.index,
+ instruction.name,
+ instruction.value,
+ instruction.priority
+ );
+ break;
+
+ case "remove":
+ rewriter.removeProperty(instruction.index, instruction.name);
+ break;
+
+ default:
+ throw new Error("unrecognized instruction");
+ }
+
+ return rewriter.getResult();
+}
+
+function run_test() {
+ for (const test of TEST_DATA) {
+ const { changed, text } = rewriteDeclarations(
+ test.input,
+ test.instruction,
+ "\t"
+ );
+ equal(text, test.expected, "output for " + test.desc);
+
+ let expectChanged;
+ if ("changed" in test) {
+ expectChanged = test.changed;
+ } else {
+ expectChanged = {};
+ }
+ deepEqual(changed, expectChanged, "changed result for " + test.desc);
+ }
+}