mirror of
https://git.proxmox.com/git/pve-eslint
synced 2025-08-23 17:26:14 +00:00
476 lines
19 KiB
JavaScript
476 lines
19 KiB
JavaScript
/*
|
|
* STOP!!! DO NOT MODIFY.
|
|
*
|
|
* This file is part of the ongoing work to move the eslintrc-style config
|
|
* system into the @eslint/eslintrc package. This file needs to remain
|
|
* unchanged in order for this work to proceed.
|
|
*
|
|
* If you think you need to change this file, please contact @nzakas first.
|
|
*
|
|
* Thanks in advance for your cooperation.
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Tests for config validator.
|
|
* @author Brandon Mills
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Requirements
|
|
//------------------------------------------------------------------------------
|
|
|
|
const assert = require("chai").assert,
|
|
{ Linter } = require("../../../lib/linter"),
|
|
validator = require("../../../lib/shared/config-validator"),
|
|
Rules = require("../../../lib/linter/rules");
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
const linter = new Linter();
|
|
|
|
const mockRule = {
|
|
meta: {
|
|
schema: [{
|
|
enum: ["first", "second"]
|
|
}]
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report(node, "Expected a validation error.");
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
const mockObjectRule = {
|
|
meta: {
|
|
schema: {
|
|
enum: ["first", "second"]
|
|
}
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report(node, "Expected a validation error.");
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
const mockNoOptionsRule = {
|
|
meta: {
|
|
schema: []
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report(node, "Expected a validation error.");
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
const mockRequiredOptionsRule = {
|
|
meta: {
|
|
schema: {
|
|
type: "array",
|
|
minItems: 1
|
|
}
|
|
},
|
|
create(context) {
|
|
return {
|
|
Program(node) {
|
|
context.report(node, "Expected a validation error.");
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Tests
|
|
//------------------------------------------------------------------------------
|
|
|
|
describe("Validator", () => {
|
|
|
|
/**
|
|
* Gets a loaded rule given a rule ID
|
|
* @param {string} ruleId The ID of the rule
|
|
* @returns {{create: Function}} The loaded rule
|
|
*/
|
|
function ruleMapper(ruleId) {
|
|
return linter.getRules().get(ruleId) || new Rules().get(ruleId);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
linter.defineRule("mock-rule", mockRule);
|
|
linter.defineRule("mock-required-options-rule", mockRequiredOptionsRule);
|
|
});
|
|
|
|
describe("validate", () => {
|
|
|
|
it("should do nothing with an empty config", () => {
|
|
validator.validate({}, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid eslint config", () => {
|
|
validator.validate(
|
|
{
|
|
$schema: "http://json.schemastore.org/eslintrc",
|
|
root: true,
|
|
globals: { globalFoo: "readonly" },
|
|
parser: "parserFoo",
|
|
env: { browser: true },
|
|
plugins: ["pluginFoo", "pluginBar"],
|
|
settings: { foo: "bar" },
|
|
extends: ["configFoo", "configBar"],
|
|
parserOptions: { foo: "bar" },
|
|
rules: {}
|
|
},
|
|
"tests",
|
|
ruleMapper
|
|
);
|
|
});
|
|
|
|
it("should throw with an unknown property", () => {
|
|
const fn = validator.validate.bind(
|
|
null,
|
|
{
|
|
foo: true
|
|
},
|
|
"tests",
|
|
ruleMapper
|
|
);
|
|
|
|
assert.throws(fn, "Unexpected top-level property \"foo\".");
|
|
});
|
|
|
|
describe("root", () => {
|
|
it("should throw with a string value", () => {
|
|
const fn = validator.validate.bind(null, { root: "true" }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `\"true\"`).");
|
|
});
|
|
|
|
it("should throw with a numeric value", () => {
|
|
const fn = validator.validate.bind(null, { root: 0 }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `0`).");
|
|
});
|
|
});
|
|
|
|
describe("globals", () => {
|
|
it("should throw with a string value", () => {
|
|
const fn = validator.validate.bind(null, { globals: "jQuery" }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `\"jQuery\"`).");
|
|
});
|
|
|
|
it("should throw with an array value", () => {
|
|
const fn = validator.validate.bind(null, { globals: ["jQuery"] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `[\"jQuery\"]`).");
|
|
});
|
|
});
|
|
|
|
describe("parser", () => {
|
|
it("should not throw with a null value", () => {
|
|
validator.validate({ parser: null }, null, ruleMapper);
|
|
});
|
|
});
|
|
|
|
describe("env", () => {
|
|
|
|
it("should throw with an array environment", () => {
|
|
const fn = validator.validate.bind(null, { env: [] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `[]`).");
|
|
});
|
|
|
|
it("should throw with a primitive environment", () => {
|
|
const fn = validator.validate.bind(null, { env: 1 }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `1`).");
|
|
});
|
|
|
|
it("should catch invalid environments", () => {
|
|
const fn = validator.validate.bind(null, { env: { browser: true, invalid: true } }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Environment key \"invalid\" is unknown\n");
|
|
});
|
|
|
|
it("should catch disabled invalid environments", () => {
|
|
const fn = validator.validate.bind(null, { env: { browser: true, invalid: false } }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Environment key \"invalid\" is unknown\n");
|
|
});
|
|
|
|
it("should do nothing with an undefined environment", () => {
|
|
validator.validate({}, null, ruleMapper);
|
|
});
|
|
|
|
});
|
|
|
|
describe("plugins", () => {
|
|
it("should not throw with an empty array", () => {
|
|
validator.validate({ plugins: [] }, null, ruleMapper);
|
|
});
|
|
|
|
it("should throw with a string", () => {
|
|
const fn = validator.validate.bind(null, { plugins: "react" }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"plugins\" is the wrong type (expected array but got `\"react\"`).");
|
|
});
|
|
});
|
|
|
|
describe("settings", () => {
|
|
it("should not throw with an empty object", () => {
|
|
validator.validate({ settings: {} }, null, ruleMapper);
|
|
});
|
|
|
|
it("should throw with an array", () => {
|
|
const fn = validator.validate.bind(null, { settings: ["foo"] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"settings\" is the wrong type (expected object but got `[\"foo\"]`).");
|
|
});
|
|
});
|
|
|
|
describe("extends", () => {
|
|
it("should not throw with an empty array", () => {
|
|
validator.validate({ extends: [] }, null, ruleMapper);
|
|
});
|
|
|
|
it("should not throw with a string", () => {
|
|
validator.validate({ extends: "react" }, null, ruleMapper);
|
|
});
|
|
|
|
it("should throw with an object", () => {
|
|
const fn = validator.validate.bind(null, { extends: {} }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "ESLint configuration in null is invalid:\n\t- Property \"extends\" is the wrong type (expected string but got `{}`).\n\t- Property \"extends\" is the wrong type (expected array but got `{}`).\n\t- \"extends\" should match exactly one schema in oneOf. Value: {}.");
|
|
});
|
|
});
|
|
|
|
describe("parserOptions", () => {
|
|
it("should not throw with an empty object", () => {
|
|
validator.validate({ parserOptions: {} }, null, ruleMapper);
|
|
});
|
|
|
|
it("should throw with an array", () => {
|
|
const fn = validator.validate.bind(null, { parserOptions: ["foo"] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Property \"parserOptions\" is the wrong type (expected object but got `[\"foo\"]`).");
|
|
});
|
|
});
|
|
|
|
describe("rules", () => {
|
|
|
|
it("should do nothing with an empty rules object", () => {
|
|
validator.validate({ rules: {} }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config with rules", () => {
|
|
validator.validate({ rules: { "mock-rule": [2, "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is off", () => {
|
|
validator.validate({ rules: { "mock-rule": ["off", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with an invalid config when severity is off", () => {
|
|
validator.validate({ rules: { "mock-required-options-rule": "off" } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with an invalid config when severity is an array with 'off'", () => {
|
|
validator.validate({ rules: { "mock-required-options-rule": ["off"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is warn", () => {
|
|
validator.validate({ rules: { "mock-rule": ["warn", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is error", () => {
|
|
validator.validate({ rules: { "mock-rule": ["error", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is Off", () => {
|
|
validator.validate({ rules: { "mock-rule": ["Off", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is Warn", () => {
|
|
validator.validate({ rules: { "mock-rule": ["Warn", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should do nothing with a valid config when severity is Error", () => {
|
|
validator.validate({ rules: { "mock-rule": ["Error", "second"] } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should catch invalid rule options", () => {
|
|
const fn = validator.validate.bind(null, { rules: { "mock-rule": [3, "third"] } }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
|
|
});
|
|
|
|
it("should allow for rules with no options", () => {
|
|
linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
|
|
|
|
validator.validate({ rules: { "mock-no-options-rule": 2 } }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should not allow options for rules with no options", () => {
|
|
linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
|
|
|
|
const fn = validator.validate.bind(null, { rules: { "mock-no-options-rule": [2, "extra"] } }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n");
|
|
});
|
|
});
|
|
|
|
describe("globals", () => {
|
|
it("should disallow globals set to invalid values", () => {
|
|
assert.throws(
|
|
() => validator.validate({ globals: { foo: "AAAAA" } }, "tests", ruleMapper),
|
|
"ESLint configuration of global 'foo' in tests is invalid:\n'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("overrides", () => {
|
|
it("should not throw with an empty overrides array", () => {
|
|
validator.validate({ overrides: [] }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should not throw with a valid overrides array", () => {
|
|
validator.validate({ overrides: [{ files: "*", rules: {} }] }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should throw if override does not specify files", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n");
|
|
});
|
|
|
|
it("should throw if override has an empty files array", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have fewer than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n");
|
|
});
|
|
|
|
it("should not throw if override has nested overrides", () => {
|
|
validator.validate({ overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should not throw if override extends", () => {
|
|
validator.validate({ overrides: [{ files: "*", extends: "eslint-recommended" }] }, "tests", ruleMapper);
|
|
});
|
|
|
|
it("should throw if override tries to set root", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n");
|
|
});
|
|
|
|
describe("env", () => {
|
|
|
|
it("should catch invalid environments", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Environment key \"invalid\" is unknown\n");
|
|
});
|
|
|
|
it("should catch disabled invalid environments", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, null, ruleMapper);
|
|
|
|
assert.throws(fn, "Environment key \"invalid\" is unknown\n");
|
|
});
|
|
|
|
});
|
|
|
|
describe("rules", () => {
|
|
|
|
it("should catch invalid rule options", () => {
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
|
|
});
|
|
|
|
it("should not allow options for rules with no options", () => {
|
|
linter.defineRule("mock-no-options-rule", mockNoOptionsRule);
|
|
|
|
const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, "tests", ruleMapper);
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n");
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe("getRuleOptionsSchema", () => {
|
|
|
|
it("should return null for a missing rule", () => {
|
|
assert.strictEqual(validator.getRuleOptionsSchema(ruleMapper("non-existent-rule")), null);
|
|
});
|
|
|
|
it("should not modify object schema", () => {
|
|
linter.defineRule("mock-object-rule", mockObjectRule);
|
|
assert.deepStrictEqual(validator.getRuleOptionsSchema(ruleMapper("mock-object-rule")), {
|
|
enum: ["first", "second"]
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe("validateRuleOptions", () => {
|
|
|
|
it("should throw for incorrect warning level number", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", 3, "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
|
|
});
|
|
|
|
it("should throw for incorrect warning level string", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", "booya", "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '\"booya\"').\n");
|
|
});
|
|
|
|
it("should throw for invalid-type warning level", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [["error"]], "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '[ \"error\" ]').\n");
|
|
});
|
|
|
|
it("should only check warning level for nonexistent rules", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("non-existent-rule"), "non-existent-rule", [3, "foobar"], "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"non-existent-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
|
|
});
|
|
|
|
it("should only check warning level for plugin rules", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("plugin/rule"), "plugin/rule", 3, "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"plugin/rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n");
|
|
});
|
|
|
|
it("should throw for incorrect configuration values", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [2, "frist"], "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tValue \"frist\" should be equal to one of the allowed values.\n");
|
|
});
|
|
|
|
it("should throw for too many configuration values", () => {
|
|
const fn = validator.validateRuleOptions.bind(null, ruleMapper("mock-rule"), "mock-rule", [2, "first", "second"], "tests");
|
|
|
|
assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tValue [\"first\",\"second\"] should NOT have more than 1 items.\n");
|
|
});
|
|
|
|
});
|
|
|
|
});
|