pve-eslint/eslint/tests/lib/linter/source-code-fixer.js
Dominik Csapak eb39fafa4f first commit
includes a (minimal) working wrapper

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2020-04-06 15:06:03 +02:00

643 lines
25 KiB
JavaScript

/**
* @fileoverview Tests for SourceCodeFixer.
* @author Nicholas C. Zakas
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const assert = require("chai").assert,
sinon = require("sinon"),
SourceCodeFixer = require("../../../lib/linter/source-code-fixer");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const TEST_CODE = "var answer = 6 * 7;";
const INSERT_AT_END = {
message: "End",
fix: {
range: [TEST_CODE.length, TEST_CODE.length],
text: "// end"
}
},
INSERT_AT_START = {
message: "Start",
fix: {
range: [0, 0],
text: "// start\n"
}
},
INSERT_IN_MIDDLE = {
message: "Multiply",
fix: {
range: [13, 13],
text: "5 *"
}
},
REPLACE_ID = {
message: "foo",
fix: {
range: [4, 10],
text: "foo"
}
},
REPLACE_VAR = {
message: "let",
fix: {
range: [0, 3],
text: "let"
}
},
REPLACE_NUM = {
message: "5",
fix: {
range: [13, 14],
text: "5"
}
},
REMOVE_START = {
message: "removestart",
fix: {
range: [0, 4],
text: ""
}
},
REMOVE_MIDDLE = {
message: "removemiddle",
fix: {
range: [5, 10],
text: ""
}
},
REMOVE_END = {
message: "removeend",
fix: {
range: [14, 18],
text: ""
}
},
NO_FIX = {
message: "nofix"
},
INSERT_BOM = {
message: "insert-bom",
fix: {
range: [0, 0],
text: "\uFEFF"
}
},
INSERT_BOM_WITH_TEXT = {
message: "insert-bom",
fix: {
range: [0, 0],
text: "\uFEFF// start\n"
}
},
REMOVE_BOM = {
message: "remove-bom",
fix: {
range: [-1, 0],
text: ""
}
},
REPLACE_BOM_WITH_TEXT = {
message: "remove-bom",
fix: {
range: [-1, 0],
text: "// start\n"
}
},
NO_FIX1 = {
message: "nofix1",
line: 1,
column: 3
},
NO_FIX2 = {
message: "nofix2",
line: 1,
column: 7
},
REVERSED_RANGE = {
message: "reversed range",
fix: {
range: [3, 0],
text: " "
}
};
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
describe("SourceCodeFixer", () => {
describe("constructor", () => {
it("Should not be able to add anything to this", () => {
const result = new SourceCodeFixer();
assert.throws(() => {
result.test = 1;
});
});
});
describe("applyFixes() with no BOM", () => {
describe("shouldFix parameter", () => {
it("Should not perform any fixes if 'shouldFix' is false", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_END], false);
assert.isFalse(result.fixed);
assert.strictEqual(result.output, TEST_CODE);
});
it("Should perform fixes if 'shouldFix' is not provided", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_END]);
assert.isTrue(result.fixed);
});
it("should call a function provided as 'shouldFix' for each message", () => {
const shouldFixSpy = sinon.spy();
SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_IN_MIDDLE, INSERT_AT_START, INSERT_AT_END], shouldFixSpy);
assert.isTrue(shouldFixSpy.calledThrice);
});
it("should provide a message object as an argument to 'shouldFix'", () => {
const shouldFixSpy = sinon.spy();
SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START], shouldFixSpy);
assert.strictEqual(shouldFixSpy.firstCall.args[0], INSERT_AT_START);
});
it("should not perform fixes if 'shouldFix' function returns false", () => {
const shouldFixSpy = sinon.spy(() => false);
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START], shouldFixSpy);
assert.isFalse(result.fixed);
});
it("should return original text as output if 'shouldFix' function prevents all fixes", () => {
const shouldFixSpy = sinon.spy(() => false);
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START], shouldFixSpy);
assert.strictEqual(result.output, TEST_CODE);
});
it("should only apply fixes for which the 'shouldFix' function returns true", () => {
const shouldFixSpy = sinon.spy(problem => problem.message === "foo");
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START, REPLACE_ID], shouldFixSpy);
assert.strictEqual(result.output, "var foo = 6 * 7;");
});
it("is called without access to internal eslint state", () => {
const shouldFixSpy = sinon.spy();
SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START], shouldFixSpy);
assert.isUndefined(shouldFixSpy.thisValues[0]);
});
});
describe("Text Insertion", () => {
it("should insert text at the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_END]);
assert.strictEqual(result.output, TEST_CODE + INSERT_AT_END.fix.text);
assert.strictEqual(result.messages.length, 0);
});
it("should insert text at the beginning of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_START]);
assert.strictEqual(result.output, INSERT_AT_START.fix.text + TEST_CODE);
assert.strictEqual(result.messages.length, 0);
});
it("should insert text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_IN_MIDDLE]);
assert.strictEqual(result.output, TEST_CODE.replace("6 *", `${INSERT_IN_MIDDLE.fix.text}6 *`));
assert.strictEqual(result.messages.length, 0);
});
it("should insert text at the beginning, middle, and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_IN_MIDDLE, INSERT_AT_START, INSERT_AT_END]);
assert.strictEqual(result.output, INSERT_AT_START.fix.text + TEST_CODE.replace("6 *", `${INSERT_IN_MIDDLE.fix.text}6 *`) + INSERT_AT_END.fix.text);
assert.strictEqual(result.messages.length, 0);
});
it("should ignore reversed ranges", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REVERSED_RANGE]);
assert.strictEqual(result.output, TEST_CODE);
});
});
describe("Text Replacement", () => {
it("should replace text at the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_VAR]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace("var", "let"));
assert.isTrue(result.fixed);
});
it("should replace text at the beginning of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_ID]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace("answer", "foo"));
assert.isTrue(result.fixed);
});
it("should replace text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_NUM]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace("6", "5"));
assert.isTrue(result.fixed);
});
it("should replace text at the beginning and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_ID, REPLACE_VAR, REPLACE_NUM]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, "let foo = 5 * 7;");
assert.isTrue(result.fixed);
});
});
describe("Text Removal", () => {
it("should remove text at the start of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_START]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace("var ", ""));
assert.isTrue(result.fixed);
});
it("should remove text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_MIDDLE]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace("answer", "a"));
assert.isTrue(result.fixed);
});
it("should remove text towards the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_END]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, TEST_CODE.replace(" * 7", ""));
assert.isTrue(result.fixed);
});
it("should remove text at the beginning, middle, and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_END, REMOVE_START, REMOVE_MIDDLE]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, "a = 6;");
assert.isTrue(result.fixed);
});
});
describe("Combination", () => {
it("should replace text at the beginning, remove text in the middle, and insert text at the end", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_AT_END, REMOVE_END, REPLACE_VAR]);
assert.strictEqual(result.output, "let answer = 6;// end");
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should only apply one fix when ranges overlap", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_MIDDLE, REPLACE_ID]);
assert.strictEqual(result.output, TEST_CODE.replace("answer", "foo"));
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "removemiddle");
assert.isTrue(result.fixed);
});
it("should apply one fix when the end of one range is the same as the start of a previous range overlap", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_START, REPLACE_ID]);
assert.strictEqual(result.output, TEST_CODE.replace("var ", ""));
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "foo");
assert.isTrue(result.fixed);
});
it("should only apply one fix when ranges overlap and one message has no fix", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_MIDDLE, REPLACE_ID, NO_FIX]);
assert.strictEqual(result.output, TEST_CODE.replace("answer", "foo"));
assert.strictEqual(result.messages.length, 2);
assert.strictEqual(result.messages[0].message, "nofix");
assert.strictEqual(result.messages[1].message, "removemiddle");
assert.isTrue(result.fixed);
});
it("should apply the same fix when ranges overlap regardless of order", () => {
const result1 = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_MIDDLE, REPLACE_ID]);
const result2 = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_ID, REMOVE_MIDDLE]);
assert.strictEqual(result1.output, result2.output);
});
});
describe("No Fixes", () => {
it("should only apply one fix when ranges overlap and one message has no fix", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [NO_FIX]);
assert.strictEqual(result.output, TEST_CODE);
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "nofix");
assert.isFalse(result.fixed);
});
it("should sort the no fix messages correctly", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_ID, NO_FIX2, NO_FIX1]);
assert.strictEqual(result.output, TEST_CODE.replace("answer", "foo"));
assert.strictEqual(result.messages.length, 2);
assert.strictEqual(result.messages[0].message, "nofix1");
assert.strictEqual(result.messages[1].message, "nofix2");
assert.isTrue(result.fixed);
});
});
describe("BOM manipulations", () => {
it("should insert BOM with an insertion of '\uFEFF' at 0", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_BOM]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should insert BOM with an insertion of '\uFEFFfoobar' at 0", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [INSERT_BOM_WITH_TEXT]);
assert.strictEqual(result.output, `\uFEFF// start\n${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should remove BOM with a negative range", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REMOVE_BOM]);
assert.strictEqual(result.output, TEST_CODE);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should replace BOM with a negative range and 'foobar'", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE, [REPLACE_BOM_WITH_TEXT]);
assert.strictEqual(result.output, `// start\n${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
});
});
/*
* This section is almost same as "with no BOM".
* Just `result.output` has BOM.
*/
describe("applyFixes() with BOM", () => {
const TEST_CODE_WITH_BOM = `\uFEFF${TEST_CODE}`;
describe("Text Insertion", () => {
it("should insert text at the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_AT_END]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE}${INSERT_AT_END.fix.text}`);
assert.strictEqual(result.messages.length, 0);
});
it("should insert text at the beginning of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_AT_START]);
assert.strictEqual(result.output, `\uFEFF${INSERT_AT_START.fix.text}${TEST_CODE}`);
assert.strictEqual(result.messages.length, 0);
});
it("should insert text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_IN_MIDDLE]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("6 *", `${INSERT_IN_MIDDLE.fix.text}6 *`)}`);
assert.strictEqual(result.messages.length, 0);
});
it("should insert text at the beginning, middle, and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_IN_MIDDLE, INSERT_AT_START, INSERT_AT_END]);
const insertInMiddle = TEST_CODE.replace("6 *", `${INSERT_IN_MIDDLE.fix.text}6 *`);
assert.strictEqual(result.output, `\uFEFF${INSERT_AT_START.fix.text}${insertInMiddle}${INSERT_AT_END.fix.text}`);
assert.strictEqual(result.messages.length, 0);
});
it("should ignore reversed ranges", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REVERSED_RANGE]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE}`);
});
});
describe("Text Replacement", () => {
it("should replace text at the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_VAR]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("var", "let")}`);
assert.isTrue(result.fixed);
});
it("should replace text at the beginning of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_ID]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("answer", "foo")}`);
assert.isTrue(result.fixed);
});
it("should replace text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_NUM]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("6", "5")}`);
assert.isTrue(result.fixed);
});
it("should replace text at the beginning and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_ID, REPLACE_VAR, REPLACE_NUM]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, "\uFEFFlet foo = 5 * 7;");
assert.isTrue(result.fixed);
});
});
describe("Text Removal", () => {
it("should remove text at the start of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_START]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("var ", "")}`);
assert.isTrue(result.fixed);
});
it("should remove text in the middle of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_MIDDLE]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("answer", "a")}`);
assert.isTrue(result.fixed);
});
it("should remove text towards the end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_END]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace(" * 7", "")}`);
assert.isTrue(result.fixed);
});
it("should remove text at the beginning, middle, and end of the code", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_END, REMOVE_START, REMOVE_MIDDLE]);
assert.strictEqual(result.messages.length, 0);
assert.strictEqual(result.output, "\uFEFFa = 6;");
assert.isTrue(result.fixed);
});
});
describe("Combination", () => {
it("should replace text at the beginning, remove text in the middle, and insert text at the end", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_AT_END, REMOVE_END, REPLACE_VAR]);
assert.strictEqual(result.output, "\uFEFFlet answer = 6;// end");
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should only apply one fix when ranges overlap", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_MIDDLE, REPLACE_ID]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("answer", "foo")}`);
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "removemiddle");
assert.isTrue(result.fixed);
});
it("should apply one fix when the end of one range is the same as the start of a previous range overlap", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_START, REPLACE_ID]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("var ", "")}`);
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "foo");
assert.isTrue(result.fixed);
});
it("should only apply one fix when ranges overlap and one message has no fix", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_MIDDLE, REPLACE_ID, NO_FIX]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE.replace("answer", "foo")}`);
assert.strictEqual(result.messages.length, 2);
assert.strictEqual(result.messages[0].message, "nofix");
assert.strictEqual(result.messages[1].message, "removemiddle");
assert.isTrue(result.fixed);
});
it("should apply the same fix when ranges overlap regardless of order", () => {
const result1 = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_MIDDLE, REPLACE_ID]);
const result2 = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_ID, REMOVE_MIDDLE]);
assert.strictEqual(result1.output, result2.output);
});
});
describe("No Fixes", () => {
it("should only apply one fix when ranges overlap and one message has no fix", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [NO_FIX]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE}`);
assert.strictEqual(result.messages.length, 1);
assert.strictEqual(result.messages[0].message, "nofix");
assert.isFalse(result.fixed);
});
});
describe("BOM manipulations", () => {
it("should insert BOM with an insertion of '\uFEFF' at 0", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_BOM]);
assert.strictEqual(result.output, `\uFEFF${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should insert BOM with an insertion of '\uFEFFfoobar' at 0", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [INSERT_BOM_WITH_TEXT]);
assert.strictEqual(result.output, `\uFEFF// start\n${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should remove BOM with a negative range", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REMOVE_BOM]);
assert.strictEqual(result.output, TEST_CODE);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
it("should replace BOM with a negative range and 'foobar'", () => {
const result = SourceCodeFixer.applyFixes(TEST_CODE_WITH_BOM, [REPLACE_BOM_WITH_TEXT]);
assert.strictEqual(result.output, `// start\n${TEST_CODE}`);
assert.isTrue(result.fixed);
assert.strictEqual(result.messages.length, 0);
});
});
});
});