diff --git a/lib/config.js b/lib/config.js index ece4b17e0..83de61c24 100644 --- a/lib/config.js +++ b/lib/config.js @@ -155,7 +155,7 @@ class Config { if (shouldActiveRule(config[rule.name])) { let ruleConfig = extractRuleConfig(config[rule.name], rule.name); this.activatedRules[rule.name] = rule; - if (ruleConfig) { + if (ruleConfig !== null && ruleConfig !== undefined) { ruleConfig = rule.configTransform ? rule.configTransform(ruleConfig) : ruleConfig; if (rule.validateConfig) { rule.validateConfig(ruleConfig); diff --git a/lib/rules/attr-bans/__tests__/index.js b/lib/rules/attr-bans/__tests__/index.js index 98d019445..244f47e48 100644 --- a/lib/rules/attr-bans/__tests__/index.js +++ b/lib/rules/attr-bans/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-bans", function() { +describe("legacy linter | attr-bans", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error for a tag named 'style'", async function() { const linter = createLinter(); const html = ""; @@ -64,3 +64,96 @@ describe("attr-bans", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("attr-bans", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + + it("Should not report an error for a tag named 'style'", async function() { + const linter = createLinter({ "attr-bans": [true, "style"] }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report anything when disabled", async function() { + const linter = createLinter({ + "attr-bans": false + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should accept a single string as option", async function() { + const linter = createLinter({ + "attr-bans": [ + true, + "style" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Banned attributes should be case insensitive", async function() { + const linter = createLinter({ + "attr-bans": [ + true, + ["ban0", "bAN1"] + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should accept regexes as config", async function() { + const linter = createLinter({ + "attr-bans": [ + true, + [/on\w+/i] + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should throw an error for an invalid config", async function() { + const config = { + "attr-bans": [ + true, + true + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-bans\" is invalid: Expected string, RegExp or (string|RegExp)[] got boolean"); + }); + + it("Should throw an error if not given a list of strings as config", function() { + const config = { + "attr-bans": [ + true, + ["string", true] + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-bans\" is invalid: Expected string, RegExp or (string|RegExp)[] got boolean[]"); + }); + + it("Should report an error when the 'style' attribute is present", async function() { + const linter = createLinter({ + "attr-bans": [ + true, + "style" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/attr-name-style/__tests__/index.js b/lib/rules/attr-name-style/__tests__/index.js index 3b10a3d50..271560aaf 100644 --- a/lib/rules/attr-name-style/__tests__/index.js +++ b/lib/rules/attr-name-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-name-style", function() { +describe("legacy linter | attr-name-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should ignore attributes matching \"raw-ignore-text\"", async function() { const linter = createLinter(); const html = "
"; @@ -104,6 +104,175 @@ describe("attr-name-style", function() { }); }); }); +describe("attr-name-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should ignore attributes matching \"raw-ignore-text\"", async function() { + const linter = linthtml.fromConfig({ + "raw-ignore-regex": "{{.*?}}", + rules: { + "attr-name-style": [ + true, + "dash" + ] + } + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report anything for correctly styled attribute names", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should ignore ignored attributes", async function() { + const linter = linthtml.fromConfig({ + "attr-name-ignore-regex": "xlink:href", + rules: { + "attr-name-style": [ + true, + "dash" + ] + } + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report anything when disabled", async function() { + const linter = createLinter({ + "attr-name-style": false + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + describe("'lowercase' format", function() { + it("Should not report an error for attributes with valid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for attributes with invalid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + }); + describe("'dash' format", function() { + it("Should not report an error for attributes with valid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for attributes with invalid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + }); + + it("Should throw an error when an invalid config is passed", function() { + const config = { + "attr-name-style": [ + true, + ["camel"] + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-name-style\" is invalid: Expected string or RegExp got object"); + }); + + describe("'regexp' format", function() { + it("Should not report an error for attributes with valid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + /^[0-9a-o]+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for attributes with invalid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + /^[0-9a-o]+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + describe("'camel' format", function() { + it("Should not report an error for attributes with valid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "camel" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for attributes with invalid format", async function() { + const linter = createLinter({ + "attr-name-style": [ + true, + "camel" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); +}); // NOPE // { // input: '
', diff --git a/lib/rules/attr-new-line/__tests__/index.js b/lib/rules/attr-new-line/__tests__/index.js index 7f29fb42c..371b4ee8f 100644 --- a/lib/rules/attr-new-line/__tests__/index.js +++ b/lib/rules/attr-new-line/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-new-line", function() { +describe("legacy linter | attr-new-line", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report errors if the number of atributes is less or equal to the configuration", async function() { const linter = createLinter(); const html = ` @@ -93,3 +93,133 @@ describe("attr-new-line", function() { .throw("Configuration for rule \"attr-new-line\" is invalid: Expected number or \"+0\" got string"); }); }); +describe("attr-new-line", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report errors if the number of atributes is less or equal to the configuration", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + 2 + ] + }); + const html = ` +
+
+ `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors if the number of attributes is superior to the rule's configuration", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + 1 + ] + }); + const html = ` +
+ `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report errors when attributes are on new lines", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + 1 + ] + }); + const html = ` +
+ `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should accept less attributes per line than the value defined in the configuration", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + 2 + ] + }); + const html = ` +
+ `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when there's one attribute on the first line and configuration is '0'", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + 0 + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error when there's one attribute on the first line and configuration is '+0'", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + "+0" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when there's more than one attribute on the first line and configuration is '+0'", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + "+0" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when there's attributes on line > 1 and configuration is '+0'", async function() { + const linter = createLinter({ + "attr-new-line": [ + true, + "+0" + ] + }); + const html = ` +
+
+ `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should throw an error when an invalid config is provided", function() { + const config = { + "attr-new-line": [ + true, + "toto" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-new-line\" is invalid: Expected number or \"+0\" got string"); + }); +}); diff --git a/lib/rules/attr-no-dup/__tests__/index.js b/lib/rules/attr-no-dup/__tests__/index.js index 579675442..ac884b10c 100644 --- a/lib/rules/attr-no-dup/__tests__/index.js +++ b/lib/rules/attr-no-dup/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-no-dup", function() { +describe("legacy linter | attr-no-dup", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error when an attribut is not duplicated", async function() { const linter = createLinter(); const html = "
"; @@ -59,3 +59,69 @@ describe("attr-no-dup", function() { .throw("Configuration for rule \"attr-no-dup\" is invalid: Expected boolean got string"); }); }); +describe("attr-no-dup", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error when an attribut is not duplicated", async function() { + const linter = createLinter({ + "attr-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when an attribut is duplicated", async function() { + const linter = createLinter({ + "attr-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should catch multiple duplicates in one element", async function() { + const linter = createLinter({ + "attr-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should catch duplicates on multiple elements", async function() { + const linter = createLinter({ + "attr-no-dup": true + }); + const html = "

Text

"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should be case insensitive", async function() { + const linter = createLinter({ + "attr-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + // handle by Config + // it("Should throw an error when an invalid config is provided", function() { + // const config = { + // "attr-no-dup": [ + // "toto" + // ] + // }; + // expect(() => createLinter(config)) + // .to + // .throw("Configuration for rule \"attr-no-dup\" is invalid: Expected boolean got string"); + // }); +}); diff --git a/lib/rules/attr-no-unsafe-char/__tests__/index.js b/lib/rules/attr-no-unsafe-char/__tests__/index.js index 034297f76..f8fa1d401 100644 --- a/lib/rules/attr-no-unsafe-char/__tests__/index.js +++ b/lib/rules/attr-no-unsafe-char/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-no-unsafe-char", function() { +describe("legacy linter | attr-no-unsafe-char", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report error for safe char in attributes", async function() { const linter = createLinter(); const html = "
"; @@ -35,4 +35,36 @@ describe("attr-no-unsafe-char", function() { }); }); +describe("attr-no-unsafe-char", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report error for safe char in attributes", async function() { + const linter = createLinter({ "attr-no-unsafe-char": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report error for tabs/new_line in attributes", async function() { + const linter = createLinter({ "attr-no-unsafe-char": true }); + const html = ` +
+
`; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report error for unsafe char in attributes", async function() { + const linter = createLinter({ "attr-no-unsafe-char": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); // \u0000 not accepted diff --git a/lib/rules/attr-order/__tests__/index.js b/lib/rules/attr-order/__tests__/index.js index 1d5a33656..b7e09a7e6 100644 --- a/lib/rules/attr-order/__tests__/index.js +++ b/lib/rules/attr-order/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-order", function() { +describe("legacy linter | attr-order", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report errors when attributes are in the correct order", async function() { const linter = createLinter(); const html = ""; @@ -114,3 +114,178 @@ describe("attr-order", function() { .throw("Configuration for rule \"attr-order\" is invalid: Expected (string|RegExp)[] got string"); }); }); + +describe("attr-order", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report errors when attributes are in the correct order", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "height", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should fail when attribute order is reversed", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report one error per misplaced attribute", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "height", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report error for attributes that are not present", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "height", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report additional errors for attributes which are not present", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "height", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should be case insensitive (OK)", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "HEIGHT", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should be case insensitive (KO)", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", "src", "HEIGHT", "width"] + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Shoud accept Regexp as config (OK)", async function() { + const linter = createLinter({ + "attr-order": [ + ["class", /^.*$/] + ] + }); + const html = ""; + + // class then everything else + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Shoud accept Regexp as config (KO)", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", /^.*$/] + ] + }); + const html = ""; + + // class then everything else + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Shoud accept multiple Regexp as config (OK)", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", /^data-.*$/, /^.*$/] + ] + }); + const html = ""; + + // class then everything else + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Shoud accept multiple Regexp as config (KO)", async function() { + const linter = createLinter({ + "attr-order": [ + true, + ["class", /^data-.*$/, /^.*$/] + ] + }); + const html = ""; + + // class then everything else + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should throw an error when an invalid config is provided", function() { + const config = { + "attr-order": [ + true, + ["class", 3] + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-order\" is invalid: Expected (string|RegExp)[] got number[]"); + }); + + it("Should throw an error when an invalid config is provided (string only)", function() { + const config = { + "attr-order": [ + true, + "class" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-order\" is invalid: Expected (string|RegExp)[] got string"); + }); +}); diff --git a/lib/rules/attr-quote-style/__tests__/index.js b/lib/rules/attr-quote-style/__tests__/index.js index b5c2c12e9..9ce3a9d6c 100644 --- a/lib/rules/attr-quote-style/__tests__/index.js +++ b/lib/rules/attr-quote-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-quote-style", function() { +describe("legacy linter | attr-quote-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error for unquoted attribute", async function() { const linter = createLinter(); const html = "
"; @@ -62,3 +62,97 @@ describe("attr-quote-style", function() { .throw("Configuration for rule \"attr-quote-style\" is invalid: Expected \"double\", \"simple\" or \"quoted\" got number."); }); }); + +describe("attr-quote-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error for unquoted attribute", async function() { + const linter = createLinter({ + "attr-quote-style": [ + true, + "quoted" + ] + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error for quoted attribute", async function() { + const linter = createLinter({ + "attr-quote-style": [ + true, + "quoted" + ] + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when invalid quote style is used (single quoted attr in double mode)", async function() { + const linter = createLinter({ + "attr-quote-style": [ + true, + "double" + ] + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when invalid quote style is used (double quoted attr in single mode)", async function() { + const linter = createLinter({ + "attr-quote-style": [ + true, + "single" + ] + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should ignore attributes with no values", async function() { + const linter = createLinter({ + "attr-quote-style": [ + true, + "single" + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error when an invalid config is provided (invalid string)", function() { + const config = { + "attr-quote-style": [ + true, + "unknown" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-quote-style\" is invalid: Expected \"double\", \"simple\" or \"quoted\" got \"unknown\"."); + }); + + it("Should throw an error when an invalid config is provided (invalid type)", function() { + const config = { + "attr-quote-style": [ + true, + 3 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"attr-quote-style\" is invalid: Expected \"double\", \"simple\" or \"quoted\" got number."); + }); +}); diff --git a/lib/rules/attr-req-value/__tests__/index.js b/lib/rules/attr-req-value/__tests__/index.js index 0e0e3b5b1..7c871ee63 100644 --- a/lib/rules/attr-req-value/__tests__/index.js +++ b/lib/rules/attr-req-value/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-req-value", function() { +describe("legacy linter | attr-req-value", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error for attribute with a value", async function() { const linter = createLinter(); const html = "
"; @@ -69,6 +69,72 @@ describe("attr-req-value", function() { }); }); +describe("attr-req-value", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error for attribute with a value", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should accept spaces in atributes value", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when an attribut didn't have a value", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when an attribut didn't have a value (with an equal sign)", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error for empty value", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report errors for bolean attributes", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors for bolean attributes with '=' but no values", async function() { + const linter = createLinter({ "attr-req-value": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); // module.exports = [ // //test htmlparser ? // desc: "should handle non-lowercase attribute names", diff --git a/lib/rules/attr-validate/__tests__/index.js b/lib/rules/attr-validate/__tests__/index.js index 8441f4d96..86078a3b1 100644 --- a/lib/rules/attr-validate/__tests__/index.js +++ b/lib/rules/attr-validate/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("attr-validate", function() { +describe("legacy linter | attr-validate", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when given malformed attributes", async function() { const linter = createLinter(); const html = "
"; @@ -30,6 +30,34 @@ describe("attr-validate", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("attr-validate", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when given malformed attributes", async function() { + const linter = createLinter({ "attr-validate": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report only one error per malformed attributes", async function() { + const linter = createLinter({ "attr-validate": true }); + const html = "

text

"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report an error for self-closing tags with no space before", async function() { + const linter = createLinter({ "attr-validate": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); // module.exports = [ // { diff --git a/lib/rules/button-req-content/__tests__/index.js b/lib/rules/button-req-content/__tests__/index.js index bd3d1fb0e..488e4f1bb 100644 --- a/lib/rules/button-req-content/__tests__/index.js +++ b/lib/rules/button-req-content/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("button-req-content", function() { +describe("legacy linter | button-req-content", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when button has no text", async function() { const linter = createLinter(); const html = ""; @@ -71,3 +71,91 @@ describe("button-req-content", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("button-req-content", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when button has no text", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("Should report an error when button has only whitespaces has content", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("Should report an error when button has no text (deep nesting)", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("Should not report an error when button has text", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should not report an error when button has text and an html comment", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should not report an error when button has text content (deep nesting)", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should not report an error when button has text content (multiple text deep nesting)", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should report an error when button has an aria-label with no content", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error when button has an aria-label with content", async function() { + const linter = createLinter({ + "button-req-content": true + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/class-no-dup/__tests__/index.js b/lib/rules/class-no-dup/__tests__/index.js index ac089f168..19081c06a 100644 --- a/lib/rules/class-no-dup/__tests__/index.js +++ b/lib/rules/class-no-dup/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("class-no-dup", function() { +describe("legacy linter | class-no-dup", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error when there's no duplicated classes", async function() { const linter = createLinter(); const html = "
"; @@ -39,7 +39,10 @@ describe("class-no-dup", function() { }); }); -describe("class-no-dup + id-class-ignore-regexp", function() { +describe("legacy linter | class-no-dup + id-class-ignore-regexp", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report errors for duplicates classes not matching a custom separator", async function() { const linter = createLinter(); const html = "
"; @@ -64,3 +67,81 @@ describe("class-no-dup + id-class-ignore-regexp", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("class-no-dup", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error when there's no duplicated classes", async function() { + const linter = createLinter({ + "class-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors when there's duplicated classes", async function() { + const linter = createLinter({ + "class-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should catch mutliple duplicates class", async function() { + const linter = createLinter({ + "class-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should catch duplicates class even with leading and trailing whitespaces", async function() { + const linter = createLinter({ + "class-no-dup": true + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); + +describe("class-no-dup + id-class-ignore-regexp", function() { + function createLinter(ignore) { + return linthtml.fromConfig({ + "id-class-ignore-regex": ignore, + rules: { + "class-no-dup": true + } + }); + } + it("Should report errors for duplicates classes not matching a custom separator", async function() { + const linter = createLinter(/^b/); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should no report errors for duplicates classes matching a custom separator", async function() { + const linter = createLinter(/^b/); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not if `id-class-ignore-regex` contain a capturing group", async function() { + const linter = createLinter(/^(b)/); + const html = "
"; + + const issues = await linter.lint(html, none); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/class-style/__tests__/index.js b/lib/rules/class-style/__tests__/index.js index b18218c33..eb36e0fd7 100644 --- a/lib/rules/class-style/__tests__/index.js +++ b/lib/rules/class-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("class-style", function() { +describe("legacy linter | class-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for correctly formatted class", async function() { const linter = createLinter(); const html = "
"; @@ -134,3 +134,215 @@ describe("class-style", function() { .throw("Configuration for rule \"class-style\" is invalid: \"foo\" is not accepted. Accepted values are \"none\", \"lowercase\", \"underscore\", \"dash\", \"camel\" and \"bem\"."); }); }); + +describe("class-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for correctly formatted class", async function() { + const linter = createLinter({ + "class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + describe("'lowercase' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + }); + + describe("'dash' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'underscore' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "underscore" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "underscore" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'BEM' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "bem" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + "bem" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + }); + + describe("'regexp' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + /^foo-\d+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "class-style": [ + true, + /^foo-\d+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html, none); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should fallback to `id-class-style` if `class-style` is false", async function() { + const linter = createLinter({ + "class-style": false, + "id-class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not fallback to `id-class-style` if `class-style` is set to `none`", async function() { + const linter = createLinter({ + "class-style": [ + true, + "none" + ], + "id-class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error if `id-class-ignore-regex` is empty", function() { + const config = { + "id-class-ignore-regex": "" + }; + + expect(() => linthtml.fromConfig(config)) + .to + .throw("Configuration for rule \"id-class-ignore-regex\" is invalid: You provide an empty string value"); + }); + + it("Should throw an error for invalid config (wrong type)", function() { + const config = { + "class-style": [ + true, + 1 + ] + }; + + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"class-style\" is invalid: Expected string|regexp got number"); + }); + + it("Should throw an error for invalid config (invalid string value)", function() { + const config = { + "class-style": [ + true, + "foo" + ] + }; + + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"class-style\" is invalid: \"foo\" is not accepted. Accepted values are \"none\", \"lowercase\", \"underscore\", \"dash\", \"camel\" and \"bem\"."); + }); +}); diff --git a/lib/rules/doctype-first/__tests__/index.js b/lib/rules/doctype-first/__tests__/index.js index fd5c2f221..8c30af351 100644 --- a/lib/rules/doctype-first/__tests__/index.js +++ b/lib/rules/doctype-first/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("doctype-first", function() { +describe("legacy linter | doctype-first", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when DOCTYPE is first", async function() { const linter = createLinter(); const html = ` @@ -127,3 +127,157 @@ describe("doctype-first", function() { .throw("Configuration for rule \"doctype-first\" is invalid: Only \"smart\" is accepted as string value"); }); }); +describe("doctype-first", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when DOCTYPE is first", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should be case-insensitive", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when doctype is not present", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when DOCTYPE is not first", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error when there's mutiple DOCTYPE (if one is first)", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + // should report an error + it("Should not report any error if the firt element is not an html tag", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + foobar + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + // should report an error + it("Should not report any error if the firt element is a comment", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + // should report an error + it("Should report if first node is a comment an second is not the doctype", async function() { + const linter = createLinter({ "doctype-first": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + describe("`smart` mode", function() { + it("Should not report any error when there's no doctype and ", async function() { + const linter = createLinter({ + "doctype-first": [ + true, + "smart" + ] + }); + const html = ` +
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when there's no doctype but an ", async function() { + const linter = createLinter({ + "doctype-first": [ + true, + "smart" + ] + }); + const html = ` + +
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should throw an error for invalid config (wrong type)", function() { + const config = { + "doctype-first": [ + true, + 0 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"doctype-first\" is invalid: Expected boolean got number"); + }); + + it("Should throw an error for invalid config (not valid string)", function() { + const config = { + "doctype-first": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"doctype-first\" is invalid: Only \"smart\" is accepted as string value"); + }); +}); diff --git a/lib/rules/doctype-html5/__tests__/index.js b/lib/rules/doctype-html5/__tests__/index.js index 5f72ffd31..ae51f773a 100644 --- a/lib/rules/doctype-html5/__tests__/index.js +++ b/lib/rules/doctype-html5/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("doctype-html5", function() { +describe("legacy linter | doctype-html5", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for a valid html5 DOCTYPE", async function() { const linter = createLinter(); const html = ` @@ -36,7 +36,7 @@ describe("doctype-html5", function() { expect(issues).to.have.lengthOf(1); }); - it("Should not report an error", async function() { + it("Should not report an error if there's no doctype", async function() { const linter = createLinter(); const html = ` @@ -46,3 +46,47 @@ describe("doctype-html5", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("doctype-html5", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for a valid html5 DOCTYPE", async function() { + const linter = createLinter({ "doctype-html5": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when DOCTYPE is not for html5", async function() { + const linter = createLinter({ "doctype-html5": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error given a legacy doctype", async function() { + const linter = createLinter({ "doctype-html5": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error if there's not doctype", async function() { + const linter = createLinter({ "doctype-html5": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/fieldset-contains-legend/__tests__/index.js b/lib/rules/fieldset-contains-legend/__tests__/index.js index 11a162bd9..3d106a862 100644 --- a/lib/rules/fieldset-contains-legend/__tests__/index.js +++ b/lib/rules/fieldset-contains-legend/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("fieldset-contains-legend", function() { +describe("legacy linter | fieldset-contains-legend", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error when a fieldset contains a legend tag", async function() { const linter = createLinter(); const html = "
Foo
"; @@ -22,3 +22,23 @@ describe("fieldset-contains-legend", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("fieldset-contains-legend", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error when a fieldset contains a legend tag", async function() { + const linter = createLinter({ "fieldset-contains-legend": true }); + const html = "
Foo
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when a fieldset does not contains a legend tag", async function() { + const linter = createLinter({ "fieldset-contains-legend": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/fig-req-figcaption/__tests__/index.js b/lib/rules/fig-req-figcaption/__tests__/index.js index e2a2bfe7f..61c3c288d 100644 --- a/lib/rules/fig-req-figcaption/__tests__/index.js +++ b/lib/rules/fig-req-figcaption/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("fig-req-figcaption", function() { +describe("legacy linter | fig-req-figcaption", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when there's no figcaption", async function() { const linter = createLinter(); const html = ` @@ -60,3 +60,62 @@ describe("fig-req-figcaption", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("fig-req-figcaption", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when there's no figcaption", async function() { + const linter = createLinter({ "fig-req-figcaption": true }); + const html = ` +
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + // TODO: Rename test >< + it("Should report two errors", async function() { + const linter = createLinter({ "fig-req-figcaption": true }); + const html = ` +
+
+

1

2

3

4

+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should report two errors when figcaption is a sibling", async function() { + const linter = createLinter({ "fig-req-figcaption": true }); + const html = ` +
+
+ `; + + // TODO: assert messages + // first should be "figure without figcaption" + // second should be "figcaption without figcaption" + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report any error when figcaption is the last child", async function() { + const linter = createLinter({ "fig-req-figcaption": true }); + const html = ` +
+

1

+

2

+
+
+ `; + + // TODO: assert messages + // first should be "figure without figcaption" + // second should be "figcaption without figcaption" + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/focusable-tabindex-style/__tests__/index.js b/lib/rules/focusable-tabindex-style/__tests__/index.js index 1db02c7de..5026de189 100644 --- a/lib/rules/focusable-tabindex-style/__tests__/index.js +++ b/lib/rules/focusable-tabindex-style/__tests__/index.js @@ -2,11 +2,11 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("focusable-tabindex-style", function() { - it("Should report error for tag with positive tabindex", async function() { +describe("legacy linter | focusable-tabindex-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } + it("Should report errors for tag with positive tabindex", async function() { const linter = createLinter(); const html = ` @@ -14,7 +14,6 @@ describe("focusable-tabindex-style", function() { `; const issues = await linter.lint(html, none, { "focusable-tabindex-style": true }); - expect(issues).to.have.lengthOf(1); }); @@ -40,7 +39,7 @@ describe("focusable-tabindex-style", function() { expect(issues).to.have.lengthOf(0); }); - it("Should not report an error per tag with negative tabindex", async function() { + it("Should report an error per tag with a positive tabindex", async function() { const linter = createLinter(); const html = ` @@ -77,3 +76,76 @@ describe("focusable-tabindex-style", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("focusable-tabindex-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report errors for tag with positive tabindex", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error for tag without tabindex attribute", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report an error for tag with negative tabindex", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error per tag with positive tabindex", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` + + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should ignore disabled tag", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report error for non interactive elements", async function() { + const linter = createLinter({ "focusable-tabindex-style": true }); + const html = ` +
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/head-req-title/__tests__/index.js b/lib/rules/head-req-title/__tests__/index.js index 8c12275e6..a629c3981 100644 --- a/lib/rules/head-req-title/__tests__/index.js +++ b/lib/rules/head-req-title/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("head-req-title", function() { +describe("legacy linter | head-req-title", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when the head title is present", async function() { const linter = createLinter(); const html = ` @@ -79,3 +79,80 @@ describe("head-req-title", function() { }); }); }); +describe("legacy linter | head-req-title", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when the head title is present", async function() { + const linter = createLinter({ "head-req-title": true }); + const html = ` + + + Title! + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when the head title is not present", async function() { + const linter = createLinter({ "head-req-title": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when the head title is empty", async function() { + const linter = createLinter({ "head-req-title": true }); + const html = ` + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + describe("Multiple ", function() { + it("Should not report any error when one title is not empty", async function() { + const linter = createLinter({ "head-req-title": true }); + const html = ` + <html> + <head> + <title> + Foo + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report any errors when all titles are empty", async function() { + const linter = createLinter({ "head-req-title": true }); + const html = ` + + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); +}); diff --git a/lib/rules/head-valid-content-model/__tests__/index.js b/lib/rules/head-valid-content-model/__tests__/index.js index aa8a6af0b..4745cc5db 100644 --- a/lib/rules/head-valid-content-model/__tests__/index.js +++ b/lib/rules/head-valid-content-model/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("head-valid-content-model", function() { +describe("legacy linter | head-valid-content-model", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error for every invalid child", async function() { const linter = createLinter(); const html = ` @@ -65,3 +65,67 @@ describe("head-valid-content-model", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("head-valid-content-model", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error for every invalid child", async function() { + const linter = createLinter({ "head-valid-content-model": true }); + const html = ` + + +
a div
+

a paragraph

+ + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report any error when is not present", async function() { + const linter = createLinter({ "head-valid-content-model": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for valid child element", async function() { + const linter = createLinter({ "head-valid-content-model": true }); + const html = ` + + + + + + + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should not report any error for empty element", async function() { + const linter = createLinter({ "head-valid-content-model": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/href-style/__tests__/index.js b/lib/rules/href-style/__tests__/index.js index 431fcd0c3..dd0f4573b 100644 --- a/lib/rules/href-style/__tests__/index.js +++ b/lib/rules/href-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("href-style", function() { +describe("legacy linter | href-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } describe("\"absolute\" mode", function() { it("Should not report any error for absolute links", async function() { const linter = createLinter(); @@ -89,3 +89,138 @@ describe("href-style", function() { .throw("Configuration for rule \"href-style\" is invalid: \"foo\" is not accepted. Accepted values are \"absolute\" and \"relative\"."); }); }); +describe("href-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + describe("\"absolute\" mode", function() { + it("Should not report any error for absolute links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "absolute" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for relative links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "absolute" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error for empty links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "absolute" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for fragment only links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "absolute" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + }); + describe("\"relative\" mode", function() { + it("Should not report any error for relative links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "relative" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for absolute links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "relative" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error for empty links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "relative" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for fragment only links", async function() { + const linter = createLinter({ + "href-style": [ + true, + "relative" + ] + }); + const html = "A link"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + }); + + it("Should throw an error for an invalid config", function() { + const config = { + "href-style": [ + true, + true + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"href-style\" is invalid: Expected string got boolean"); + }); + + it("Should throw an error if not given a list of strings as config", function() { + const config = { + "href-style": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"href-style\" is invalid: \"foo\" is not accepted. Accepted values are \"absolute\" and \"relative\"."); + }); +}); diff --git a/lib/rules/html-valid-content-model/__tests__/index.js b/lib/rules/html-valid-content-model/__tests__/index.js index f6880fe8d..9dcdf2958 100644 --- a/lib/rules/html-valid-content-model/__tests__/index.js +++ b/lib/rules/html-valid-content-model/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("html-valid-content-model", function() { +describe("legacy linter | html-valid-content-model", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error for every invalid child", async function() { const linter = createLinter(); const html = ` @@ -89,3 +89,89 @@ describe("html-valid-content-model", function() { expect(issues).to.have.lengthOf(2); }); }); +describe("html-valid-content-model", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error for every invalid child", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + + +
A div
+

A paragraph

+ + + `; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(3); + }); + + it("Should not report any error when is missing", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + +
A div
+

A paragraph

+ + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when and are in the correct order", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when and are not in the correct order", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should accept only one as child", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should accept only one as child", async function() { + const linter = createLinter({ "html-valid-content-model": true }); + const html = ` + + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); +}); diff --git a/lib/rules/id-class-no-ad/__tests__/index.js b/lib/rules/id-class-no-ad/__tests__/index.js index 4552e032e..7e8c6dcb8 100644 --- a/lib/rules/id-class-no-ad/__tests__/index.js +++ b/lib/rules/id-class-no-ad/__tests__/index.js @@ -2,10 +2,11 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("id-class-no-ad", function() { +describe("legacy linter | id-class-no-ad", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } + describe("\"ad\" word", function() { it("Should not report any error for \"class\" attributes not containing \"ad\"", async function() { const linter = createLinter(); @@ -44,10 +45,20 @@ describe("id-class-no-ad", function() { const issues = await linter.lint(html, none, { "id-class-no-ad": true }); expect(issues).to.have.lengthOf(2); }); + + it("Should not report any error for adjacent world", async function() { + const linter = createLinter(); + const html = ` +
Foo
+ `; + + const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + expect(issues).to.have.lengthOf(0); + }); }); - describe("\"ad\" word", function() { - it("Should not report any error for \"class\" attributes not containing \"ad\"", async function() { + describe("\"social\" word", function() { + it("Should not report any error for \"class\" attributes not containing \"social\"", async function() { const linter = createLinter(); const html = "
Foo
"; @@ -55,7 +66,7 @@ describe("id-class-no-ad", function() { expect(issues).to.have.lengthOf(0); }); - it("Should not report any error for \"id\" attributes not containing \"ad\"", async function() { + it("Should not report any error for \"id\" attributes not containing \"social\"", async function() { const linter = createLinter(); const html = "
Foo
"; @@ -63,22 +74,22 @@ describe("id-class-no-ad", function() { expect(issues).to.have.lengthOf(0); }); - it("Should report an error for \"class\" attributes containing \"ad\"", async function() { + it("Should report an error for \"class\" attributes containing \"social\"", async function() { const linter = createLinter(); const html = ` -
Foo
-
Foo
+
Foo
+
Foo
`; const issues = await linter.lint(html, none, { "id-class-no-ad": true }); expect(issues).to.have.lengthOf(2); }); - it("Should report an error for \"id\" attributes containing \"ad\"", async function() { + it("Should report an error for \"id\" attributes containing \"social\"", async function() { const linter = createLinter(); const html = ` -
Foo
+
Foo
`; const issues = await linter.lint(html, none, { "id-class-no-ad": true }); @@ -88,7 +99,7 @@ describe("id-class-no-ad", function() { it("Should not report any error for adjacent world", async function() { const linter = createLinter(); const html = ` -
Foo
+
Foo
`; const issues = await linter.lint(html, none, { "id-class-no-ad": true }); @@ -96,8 +107,8 @@ describe("id-class-no-ad", function() { }); }); - describe("\"social\" word", function() { - it("Should not report any error for \"class\" attributes not containing \"social\"", async function() { + describe("\"banner\" word", function() { + it("Should not report any error for \"class\" attributes not containing \"banner\"", async function() { const linter = createLinter(); const html = "
Foo
"; @@ -105,7 +116,7 @@ describe("id-class-no-ad", function() { expect(issues).to.have.lengthOf(0); }); - it("Should not report any error for \"id\" attributes not containing \"social\"", async function() { + it("Should not report any error for \"id\" attributes not containing \"banner\"", async function() { const linter = createLinter(); const html = "
Foo
"; @@ -113,86 +124,180 @@ describe("id-class-no-ad", function() { expect(issues).to.have.lengthOf(0); }); - it("Should report an error for \"class\" attributes containing \"social\"", async function() { + it("Should report an error for \"class\" attributes containing \"banner\"", async function() { const linter = createLinter(); const html = ` + +
Foo
+ `; + + const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + expect(issues).to.have.lengthOf(2); + }); + + it("Should report an error for \"id\" attributes containing \"banner\"", async function() { + const linter = createLinter(); + const html = ` + +
Foo
+ `; + + const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + expect(issues).to.have.lengthOf(2); + }); + }); + + it("Should not report any error for adjacent world", async function() { + const linter = createLinter(); + const html = ` +
Foo
+ `; + + const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + expect(issues).to.have.lengthOf(0); + }); +}); +describe("id-class-no-ad", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + describe("\"ad\" word", function() { + it("Should not report any error for \"class\" attributes not containing \"ad\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = "
Foo
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for \"id\" attributes not containing \"ad\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = "
Foo
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for \"class\" attributes containing \"ad\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = ` +
Foo
+
Foo
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should report an error for \"id\" attributes containing \"ad\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = ` + +
Foo
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + }); + + describe("\"social\" word", function() { + it("Should not report any error for \"class\" attributes not containing \"social\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = "
Foo
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for \"id\" attributes not containing \"social\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = "
Foo
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for \"class\" attributes containing \"social\"", async function() { + const linter = createLinter({ "id-class-no-ad": true }); + const html = `
Foo
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(2); }); it("Should report an error for \"id\" attributes containing \"social\"", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = `
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(2); }); it("Should not report any error for adjacent world", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = `
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(0); }); }); describe("\"banner\" word", function() { it("Should not report any error for \"class\" attributes not containing \"banner\"", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = "
Foo
"; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(0); }); it("Should not report any error for \"id\" attributes not containing \"banner\"", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = "
Foo
"; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(0); }); it("Should report an error for \"class\" attributes containing \"banner\"", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = `
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(2); }); it("Should report an error for \"id\" attributes containing \"banner\"", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = `
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(2); }); }); it("Should not report any error for adjacent world", async function() { - const linter = createLinter(); + const linter = createLinter({ "id-class-no-ad": true }); const html = `
Foo
`; - const issues = await linter.lint(html, none, { "id-class-no-ad": true }); + const issues = await linter.lint(html); expect(issues).to.have.lengthOf(0); }); }); diff --git a/lib/rules/id-no-dup/__tests__/index.js b/lib/rules/id-no-dup/__tests__/index.js index b964fafa6..6f2642999 100644 --- a/lib/rules/id-no-dup/__tests__/index.js +++ b/lib/rules/id-no-dup/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("id-no-dup", function() { +describe("legacy linter | id-no-dup", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error when there's no duplicated id", async function() { const linter = createLinter(); const html = "
"; @@ -54,3 +54,56 @@ describe("id-no-dup", function() { // }); // }); }); + +describe("id-no-dup", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error when there's no duplicated id", async function() { + const linter = createLinter({ "id-no-dup": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors when there's duplicated id", async function() { + const linter = createLinter({ "id-no-dup": true }); + const html = ` +
+
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should catch mutliple duplicates id", async function() { + const linter = createLinter({ "id-no-dup": true }); + const html = ` +
+
+
+
+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + // TODO: should ignore trailling/leading space ? + // it("Should catch duplicates id even with leading and trailing whitespaces", async function() { + // const linter = createLinter(); + // const html = ` + //
+ //
+ //
+ //
+ // `; + + // const issues = await linter.lint(html, none, { "id-no-dup": true }); + // expect(issues).to.have.lengthOf(2); + // done(); + // }); + // }); +}); diff --git a/lib/rules/id-style/__tests__/index.js b/lib/rules/id-style/__tests__/index.js index 2cd29b22f..229e53fec 100644 --- a/lib/rules/id-style/__tests__/index.js +++ b/lib/rules/id-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("id-style", function() { +describe("legacy linter | id-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should ignore id matching \"raw-ignore-text\"", async function() { const linter = createLinter(); const html = "
"; @@ -110,3 +110,171 @@ describe("id-style", function() { .throw("Configuration for rule \"id-class-ignore-regex\" is invalid: You provide an empty string value"); }); }); +describe("id-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should ignore id matching \"raw-ignore-text\"", async function() { + const linter = linthtml.fromConfig({ + "raw-ignore-regex": "{{.*?}}", + rules: { + "id-class-style": [ + true, + "dash" + ] + } + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for correctly formatted class", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + describe("'lowercase' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "lowercase" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'dash' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "dash" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'underscore' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "underscore" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "underscore" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'BEM' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "bem" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + "bem" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("'regexp' format", function() { + it("Should not report an error for classes with valid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + /^foo-\d+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for classes with invalid format", async function() { + const linter = createLinter({ + "id-class-style": [ + true, + /^foo-\d+$/ + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should throw an error if `id-class-ignore-regex` is empty", function() { + expect(() => linthtml.fromConfig({ "id-class-ignore-regex": "" })) + .to + .throw("Configuration for rule \"id-class-ignore-regex\" is invalid: You provide an empty string value"); + }); +}); diff --git a/lib/rules/id-style/index.js b/lib/rules/id-style/index.js index 6c3f11f3b..78e358200 100644 --- a/lib/rules/id-style/index.js +++ b/lib/rules/id-style/index.js @@ -27,7 +27,7 @@ module.exports = { } }, { - name: "id-class-style", + name: "id-class-style", // REMOVE: A rule be standalone and not call other rules validateConfig(option) { if (typeof option !== "string" && isRegExp(option) === false) { throw new Error(`Configuration for rule "${this.name}" is invalid: Expected string|regexp got ${typeof option}`); @@ -40,12 +40,13 @@ module.exports = { }, rules: ["class-style", "id-style"], lint(node, opts, { report, rules }) { - if (rules["id-style"] || rules["id-class-style"]) { - return []; + if (!!rules["id-style"] === false && rules["id-class-style"]) { + const ignore = opts["id-class-ignore-regex"]; + lint(node, opts["id-class-style"], ignore, report); + } + if (!!rules["class-style"] === false && rules["id-class-style"]) { + lintClassStyle(node, opts, { report }); } - const ignore = opts["id-class-ignore-regex"]; - lint(node, opts["id-class-style"], ignore, report); - lintClassStyle(node, opts, { report }); } }, { diff --git a/lib/rules/img-req-alt/__tests__/index.js b/lib/rules/img-req-alt/__tests__/index.js index 9a52ba2e9..99c44e713 100644 --- a/lib/rules/img-req-alt/__tests__/index.js +++ b/lib/rules/img-req-alt/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("img-req-alt", function() { +describe("legacy linter | img-req-alt", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for with an alt value", async function() { const linter = createLinter(); const html = "\"A"; @@ -74,3 +74,93 @@ describe("img-req-alt", function() { .throw("Configuration for rule \"img-req-alt\" is invalid: Only \"allownull\" is accepted as string value"); }); }); + +describe("img-req-alt", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for with an alt value", async function() { + const linter = createLinter({ "img-req-alt": true }); + const html = "\"A"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for without an alt value", async function() { + const linter = createLinter({ "img-req-alt": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error for with an empty alt value", async function() { + const linter = createLinter({ "img-req-alt": true }); + const html = "\"\""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should check only ", async function() { + const linter = createLinter({ "img-req-alt": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + describe("\"allownull\" option", function() { + it("Should not report error for with an empty alt value", async function() { + const linter = createLinter({ + "img-req-alt": [ + true, + "allownull" + ] + }); + const html = "\"\""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for without an alt value", async function() { + const linter = createLinter({ + "img-req-alt": [ + true, + "allownull" + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should throw an error for invalid config (wrong type)", function() { + const config = { + "img-req-alt": [ + true, + 0 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"img-req-alt\" is invalid: Expected boolean got number"); + }); + + it("Should throw an error for invalid config (not valid string)", function() { + const config = { + "img-req-alt": [ + true, + "foo" + ] + }; + + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"img-req-alt\" is invalid: Only \"allownull\" is accepted as string value"); + }); +}); diff --git a/lib/rules/img-req-src/__tests__/index.js b/lib/rules/img-req-src/__tests__/index.js index 07466987b..feecb69c8 100644 --- a/lib/rules/img-req-src/__tests__/index.js +++ b/lib/rules/img-req-src/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("img-req-src", function() { +describe("legacy linter | img-req-src", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for with an src value", async function() { const linter = createLinter(); const html = "\"A"; @@ -38,3 +38,39 @@ describe("img-req-src", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("img-req-src", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for with an src value", async function() { + const linter = createLinter({ "img-req-src": true }); + const html = "\"A"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for without src alt value", async function() { + const linter = createLinter({ "img-req-src": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error for with an empty src value", async function() { + const linter = createLinter({ "img-req-src": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should check only ", async function() { + const linter = createLinter({ "img-req-src": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/indent-style/__tests__/index.js b/lib/rules/indent-style/__tests__/index.js index 24e149817..231abd1dc 100644 --- a/lib/rules/indent-style/__tests__/index.js +++ b/lib/rules/indent-style/__tests__/index.js @@ -4,11 +4,10 @@ const path = require("path"); const fs = require("fs"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} - -describe("indent-style", function() { +describe("legay linter | indent-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } describe("\"tabs\" style", function() { it("Should not report any error for tab indent", async function() { const linter = createLinter(); @@ -88,7 +87,10 @@ describe("indent-style", function() { }); }); -describe("\"indent-style\" + \"indent-width\"", function() { +describe("legacy linter | \"indent-style\" + \"indent-width\"", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } describe("\"tabs\" style", function() { it("Should not report any error when the correct number of tabs is used", async function() { const linter = createLinter(); @@ -185,6 +187,321 @@ describe("\"indent-style\" + \"indent-width\"", function() { }); }); +describe("indent-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + describe("\"tabs\" style", function() { + it("Should not report any error for tab indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ] + }); + const html = "
\n\t

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for space indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ] + }); + const html = "
\n

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("\"spaces\" style", function() { + it("Should not report any error for space indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ] + }); + const html = "
\n

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for tab indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ] + }); + const html = "
\n\t

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("\"nonmixed\" style", function() { + it("Should not report any error for space indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "nonmixed" + ] + }); + const html = "
\n

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for tab indent", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "nonmixed" + ] + }); + const html = "
\n\t

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when tabs and spaces are mixed on the same line", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "nonmixed" + ] + }); + const html = "
\n\t

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should throw an error if not given a string as config", function() { + const config = { + "indent-style": [ + true, + true + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"indent-style\" is invalid: Expected string got boolean"); + }); + + it("Should throw an error if not given a valid string as config", function() { + const config = { + "indent-style": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"indent-style\" is invalid: Indent style \"foo\" is not valid. Valid indent styles are \"tabs\", \"spaces\" and \"nonmixed\""); + }); +}); + +describe("\"indent-style\" + \"indent-width\"", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + describe("\"tabs\" style", function() { + it("Should not report any error when the correct number of tabs is used", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ], + "indent-width": [ + true, + 1 + ] + }); + const html = "
\n\t

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Should not report any error when the correct number of tabs is used (complex)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ], + "indent-width": [ + true, + 1 + ] + }); + const html = [ + "
", + "\t

Foo

", + "
" + ].join("\n"); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when an incorrect number of tabs is used (to many)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ], + "indent-width": [ + true, + 1 + ] + }); + const html = "
\n\t\t

foo\n

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when an incorrect number of tabs is used (not enought)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "tabs" + ], + "indent-width": [ + true, + 2 + ] + }); + const html = "
\n\t

foo\n

\n
"; + + const issues = await linter.lint(html, none, { "indent-style": "tabs", "indent-width": 2 }); + expect(issues).to.have.lengthOf(1); + }); + }); + + describe("\"spaces\" style", function() { + it("Should not report any error when the correct number of spaces is used", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ], + "indent-width": [ + true, + 2 + ] + }); + const html = "
\n

foo

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when an incorrect number of spaces is used (to many)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ], + "indent-width": [ + true, + 1 + ] + }); + const html = "
\n

foo\n

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when an incorrect number of spaces is used (not enought)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ], + "indent-width": [ + true, + 2 + ] + }); + const html = "
\n

foo\n

\n
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should not report any errors (real exemple)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ], + "indent-width": [ + true, + 2 + ] + }); + const html = fs.readFileSync(path.resolve(__dirname, "fixtures/valid.html")).toString("utf8"); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors (real exemple)", async function() { + const linter = createLinter({ + "indent-style": [ + true, + "spaces" + ], + "indent-width": [ + true, + 2 + ] + }); + const html = fs.readFileSync(path.resolve(__dirname, "fixtures/invalid.html")).toString("utf8"); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(4); + }); + + it("Should throw an error if not given a number as config", function() { + const config = { + "indent-width": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"indent-width\" is invalid: Expected number got string"); + }); + + it("Should throw an error if not given a positive number as config", function() { + const config = { + "indent-width": [ + true, + -1 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"indent-width\" is invalid: Only positive indent value are allowed"); + }); +}); + // // //shoult report an error // //

// //

diff --git a/lib/rules/indent-style/index.js b/lib/rules/indent-style/index.js index 3f9923bd8..03462c556 100644 --- a/lib/rules/indent-style/index.js +++ b/lib/rules/indent-style/index.js @@ -181,7 +181,6 @@ function check_node_indent(node, indent, expected_indent_width, report) { }); } if (check_indent_width_close(node) === false && indent_width_valid !== false) { - console.log("aie"); report({ code: "E036", position: [node.closeLineCol[0], node.closeLineCol[1]], @@ -223,7 +222,7 @@ function lint(element, opts, { report }) { } const expected_indent_width = 0; const style = opts["indent-style"] || "spaces"; - const width = opts["indent-width"]; + const width = opts["indent-width"] || false; return check_child(element, { style, width }, expected_indent_width, report); } diff --git a/lib/rules/input-btn-req-value-or-title/__tests__/index.js b/lib/rules/input-btn-req-value-or-title/__tests__/index.js index f043132a1..b5dd26154 100644 --- a/lib/rules/input-btn-req-value-or-title/__tests__/index.js +++ b/lib/rules/input-btn-req-value-or-title/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("input-btn-req-value-or-title", function() { +describe("legacy linter | input-btn-req-value-or-title", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("should fail for an input[button] without value and title", async function() { const linter = createLinter(); const html = ""; @@ -63,23 +63,64 @@ describe("input-btn-req-value-or-title", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("input-btn-req-value-or-title", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("should fail for an input[button] without value and title", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("should fail for an input[submit] without value and title", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("should fail for an input[reset] without value and title", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("should ignore inputs that are not buttons", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("should pass when input have a title", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; -// module.exports = [ -// { -// it('should pass when input have a title', -// input: '', -// opts: { -// 'input-btn-req-value-or-title': true -// }, -// output: 0 -// }, -// { -// desc: 'should pass when there is a value', -// input: '', -// opts: { -// 'input-btn-req-value-or-title': true -// }, -// output: 0 -// } - -// ]; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("should pass when input have a value", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("should pass when input have a none empty aria-label", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("should fail when input have an empty aria-label", async function() { + const linter = createLinter({ "input-btn-req-value-or-title": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/input-radio-req-name/__tests__/index.js b/lib/rules/input-radio-req-name/__tests__/index.js index 02c3aea28..efed530c9 100644 --- a/lib/rules/input-radio-req-name/__tests__/index.js +++ b/lib/rules/input-radio-req-name/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("input-radio-req-name", function() { +describe("legacy linter | input-radio-req-name", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for radio input with a name", async function() { const linter = createLinter(); const html = ""; @@ -54,3 +54,55 @@ describe("input-radio-req-name", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("legacy linter | input-radio-req-name", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for radio input with a name", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for radio input without a name", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error for radio input with an empty name value", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error if input with no type", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error if input is not a radio input", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should check only input radio ", async function() { + const linter = createLinter({ "input-radio-req-name": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/input-req-label/__tests__/index.js b/lib/rules/input-req-label/__tests__/index.js index eaae708f7..59476ff18 100644 --- a/lib/rules/input-req-label/__tests__/index.js +++ b/lib/rules/input-req-label/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("input-req-label", function() { +describe("legacy linter | input-req-label", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error for label only", async function() { const linter = createLinter(); const html = ""; @@ -46,6 +46,50 @@ describe("input-req-label", function() { expect(issues).to.have.lengthOf(0); }); }); +describe("input-req-label", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error for label only", async function() { + const linter = createLinter({ "input-req-label": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error if the text input has no attached label (parent node)", async function() { + const linter = createLinter({ "input-req-label": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error if the text input has an attached label (parent node)", async function() { + const linter = createLinter({ "input-req-label": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error if the input has a id without a matching label node", async function() { + const linter = createLinter({ "input-req-label": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error if the input has a id with a matching label node", async function() { + const linter = createLinter({ "input-req-label": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); // module.exports = [ // TODO: Should report an error diff --git a/lib/rules/label-no-enc-textarea-or-select/__tests__/index.js b/lib/rules/label-no-enc-textarea-or-select/__tests__/index.js index 9715855b5..4450e5bb0 100644 --- a/lib/rules/label-no-enc-textarea-or-select/__tests__/index.js +++ b/lib/rules/label-no-enc-textarea-or-select/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("label-no-enc-textarea-or-select", function() { +describe("legacy linter | label-no-enc-textarea-or-select", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Report an error when there's a inside a + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Report an error when there's a + + + + +
+
+ + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Report an error when there's a +
+
+ + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Report nothing when the + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/label-req-for/__tests__/index.js b/lib/rules/label-req-for/__tests__/index.js index b973d69ca..6909b80c9 100644 --- a/lib/rules/label-req-for/__tests__/index.js +++ b/lib/rules/label-req-for/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("label-req-for", function() { +describe("legacy linter | label-req-for", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when label has for value matching an existing input id", async function() { const linter = createLinter(); const html = ` @@ -95,3 +95,97 @@ describe("label-req-for", function() { expect(issues).to.have.lengthOf(1); }); }); + +describe("label-req-for", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when label has for value matching an existing input id", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when label has for value not matching an existing input id", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should be able to deal with multiple label/input", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error event when label+input are not siblings", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when label without for has a input has child node", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when label without for doesnt't have any labelable node has child", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when label has for value matching an none labelable node", async function() { + const linter = createLinter({ "label-req-for": true }); + const html = ` + +

Text content

+ `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/lang/__tests__/index.js b/lib/rules/lang/__tests__/index.js index 9315ea82e..3da08b934 100644 --- a/lib/rules/lang/__tests__/index.js +++ b/lib/rules/lang/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("html-req-lang", function() { +describe("legacy linter | html-req-lang", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when html tag as a none empty lang attribute", async function() { const linter = createLinter(); const html = ` @@ -30,7 +30,10 @@ describe("html-req-lang", function() { expect(issues).to.have.lengthOf(1); }); }); -describe("html-req-lang", function() { +describe("legacy linter | lang-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error for invalid lang code", async function() { const linter = createLinter(); const html = ` @@ -110,3 +113,132 @@ describe("html-req-lang", function() { .throw("Configuration for rule \"lang-style\" is invalid: Only \"case\" is accepted as string value"); }); }); +describe("html-req-lang", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when html tag as a none empty lang attribute", async function() { + const linter = createLinter({ "html-req-lang": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when html tag does not have a lang attribute", async function() { + const linter = createLinter({ "html-req-lang": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); +describe("lang-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error for invalid lang code", async function() { + const linter = createLinter({ "lang-style": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error for valid lang code", async function() { + const linter = createLinter({ "lang-style": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + // TODO: Should not + it("Should allow empty lang tag", async function() { + const linter = createLinter({ "lang-style": true }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error on wrong-case lang", async function() { + const linter = createLinter({ + "lang-style": [ + true, + "case" + ] + }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any for correct case lang", async function() { + const linter = createLinter({ + "lang-style": [ + true, + "case" + ] + }); + const html = ` + + + + `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error for invalid config (wrong type)", function() { + const config = { + "lang-style": [ + true, + 0 + ] + }; + + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"lang-style\" is invalid: Expected boolean got number"); + }); + + it("Should throw an error for invalid config (not valid string)", function() { + const config = { + "lang-style": [ + true, + "foo" + ] + }; + + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"lang-style\" is invalid: Only \"case\" is accepted as string value"); + }); +}); diff --git a/lib/rules/lang/index.js b/lib/rules/lang/index.js index 577d38441..e53149527 100644 --- a/lib/rules/lang/index.js +++ b/lib/rules/lang/index.js @@ -1,4 +1,4 @@ -const knife = require("../../knife"); +const { hasNonEmptyAttr, checkLangTag } = require("../../knife"); const { isTagNode } = require("../../knife/tag_utils"); module.exports = { @@ -32,7 +32,7 @@ module.exports = { return []; } - const valid = knife.checkLangTag(lang); // WHAT??? + const valid = checkLangTag(lang); // WHAT??? if (valid === 1) { report({ code: "E038", @@ -59,15 +59,14 @@ module.exports = { } }, { name: "html-req-lang", - lint(element, opts, { report }) { - if (isTagNode(element) === false || element.name !== "html") { + lint(node, opts, { report }) { + if (isTagNode(node) === false || node.name !== "html") { return; } - const attr = element.attribs; - if (!!attr || !!attr.lang) { + if (hasNonEmptyAttr(node, "lang") === false) { report({ code: "E025", - position: element.openLineCol + position: node.openLineCol }); } } @@ -94,7 +93,7 @@ function legacy_lint(element, opts, report) { } if (opts["lang-style"]) { - const valid = knife.checkLangTag(lang); // WHAT??? + const valid = checkLangTag(lang); // WHAT??? if (valid === 1) { report({ code: "E038", diff --git a/lib/rules/line-end-style/__tests__/index.js b/lib/rules/line-end-style/__tests__/index.js index 313497456..21ae98229 100644 --- a/lib/rules/line-end-style/__tests__/index.js +++ b/lib/rules/line-end-style/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("line-end-style", function() { +describe("legacy linter | line-end-style", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } describe("\"cr\" mode", function() { it("Should not report any errors for valide end line", async function() { const linter = createLinter(); @@ -119,3 +119,162 @@ describe("line-end-style", function() { .throw("Configuration for rule \"line-end-style\" is invalid: Expected \"cr\", \"lf\" or \"crlf\" got \"foo\"."); }); }); + +describe("line-end-style", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + describe("\"cr\" mode", function() { + it("Should not report any errors for valide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "cr" + ] + }); + const html = [ + "\r", + "

\r", + " some text\r", + "

\r", + "\r" + ].join(""); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report errors for invalide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "cr" + ] + }); + const html = [ + "\n", + "

\r", + " some text\r", + "

\r", + "\r" + ].join(""); + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + describe("\"lf\" mode", function() { + it("Should report errors for invalide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "lf" + ] + }); + const html = [ + "\n", + "

\n", + " some text\n", + "

\n", + "\n" + ].join(""); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for valide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "lf" + ] + }); + const html = [ + "\n", + "

\r", + " some text\r", + "

\r", + "\r" + ].join(""); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(4); + }); + }); + describe("\"crlf\" mode", function() { + it("Should report errors for invalide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "crlf" + ] + }); + const html = [ + "\r\n", + "

\r\n", + " some text\r\n", + "

\r\n", + "\r\n" + ].join(""); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for valide end line", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "crlf" + ] + }); + const html = [ + "\r\n", + "

\r\n", + " some text\r", + "

\r\n", + "\r\n" + ].join(""); + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + }); + + it("Should not report any error for just one line without end char", async function() { + const linter = createLinter({ + "line-end-style": [ + true, + "lf" + ] + }); + const html = "

foo

"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error for invalid config (not valid type)", function() { + const config = { + "line-end-style": [ + true, + 0 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"line-end-style\" is invalid: Expected a string got number."); + }); + + it("Should throw an error for invalid config (not valid string)", function() { + const config = { + "line-end-style": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"line-end-style\" is invalid: Expected \"cr\", \"lf\" or \"crlf\" got \"foo\"."); + }); +}); diff --git a/lib/rules/line-max-len/__tests__/index.js b/lib/rules/line-max-len/__tests__/index.js index ab6ebf80f..5df8ec067 100644 --- a/lib/rules/line-max-len/__tests__/index.js +++ b/lib/rules/line-max-len/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("line-max-len", function() { +describe("legacy linter | line-max-len", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when the line does not exceed the max length", async function() { const linter = createLinter(); const html = "1234"; @@ -54,6 +54,86 @@ describe("line-max-len", function() { .throw("Configuration for rule \"line-max-len\" is invalid: Only positive indent value are allowed."); }); }); +describe("line-max-len", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when the line does not exceed the max length", async function() { + const linter = createLinter({ + "line-max-len": [ + true, + 5 + ] + }); + const html = "1234"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when the line length equal the max length", async function() { + const linter = createLinter({ + "line-max-len": [ + true, + 5 + ] + }); + const html = "12345"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should support multilines", async function() { + const linter = createLinter({ + "line-max-len": [ + true, + 5 + ] + }); + const html = "12345\n12345\n1234"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when the line does exceed the max length", async function() { + const linter = createLinter({ + "line-max-len": [ + true, + 5 + ] + }); + const html = "123456"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should throw an error if not given a number as config", function() { + const config = { + "line-max-len": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"line-max-len\" is invalid: Expected number got string"); + }); + + it("Should throw an error if not given a positive number as config", function() { + const config = { + "line-max-len": [ + true, + -1 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"line-max-len\" is invalid: Only positive indent value are allowed."); + }); +}); // module.exports = [ // { diff --git a/lib/rules/line-no-trailing-whitespace/__tests__/index.js b/lib/rules/line-no-trailing-whitespace/__tests__/index.js index b9440d91c..499c4e130 100644 --- a/lib/rules/line-no-trailing-whitespace/__tests__/index.js +++ b/lib/rules/line-no-trailing-whitespace/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("line-no-trailling-whitespace", function() { +describe("legacy linter | line-no-trailling-whitespace", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when the line end with a trailling whitespace", async function() { const linter = createLinter(); const html = "1234 "; @@ -32,6 +32,36 @@ describe("line-no-trailling-whitespace", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("line-no-trailling-whitespace", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when the line end with a trailling whitespace", async function() { + const linter = createLinter({ "line-no-trailing-whitespace": true }); + const html = "1234 "; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error per line with a trailling whitespace", async function() { + const linter = createLinter({ "line-no-trailing-whitespace": true }); + const html = ` + foo + bar `; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should report only on error when line end with multiples trailling whitespace", async function() { + const linter = createLinter({ "line-no-trailing-whitespace": true }); + const html = "foo "; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); // module.exports = [ // { diff --git a/lib/rules/link-min-length-4/__tests__/index.js b/lib/rules/link-min-length-4/__tests__/index.js index 17c3bf667..2146bb860 100644 --- a/lib/rules/link-min-length-4/__tests__/index.js +++ b/lib/rules/link-min-length-4/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("link-min-length-4", function() { +describe("legacy linter | link-min-length-4", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Ignore link without href attribute", async function() { const linter = createLinter(); const html = "A"; @@ -64,3 +64,66 @@ describe("link-min-length-4", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("link-min-length-4", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Ignore link without href attribute", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = "A"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Report an error for links with text content with less than 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = "AAA"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("Report an error for links with an aria-label's content with less than 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + it("Report nothing for links with an aria-label's content with more than 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Report nothing for links with an aria-label's content with 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Report nothing for links with text content with 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = "AAAA"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Report nothing for links with text content with 4 chars", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = "AAAAA"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + it("Report nothing for links with valid text content (nested)", async function() { + const linter = createLinter({ "link-min-length-4": true }); + const html = "span>Google"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/link-req-noopener/__tests__/index.js b/lib/rules/link-req-noopener/__tests__/index.js index 92f8cfeef..4f80146a5 100644 --- a/lib/rules/link-req-noopener/__tests__/index.js +++ b/lib/rules/link-req-noopener/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("link-rel-noopener", function() { +describe("legacy linter | link-rel-noopener", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when \"target\" does not equal \"_blank\"", async function() { const linter = createLinter(); const html = "index"; @@ -54,3 +54,56 @@ describe("link-rel-noopener", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("link-rel-noopener", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when \"target\" does not equal \"_blank\"", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "index"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when \"target\" equal \"_blank\" and \"rel\" attribut is undefined", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "Site"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when \"target\" equal \"_blank\" and \"rel\" attribut is not equal to \"noopener\" or \"noreferrer\"", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "Site"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when \"target\" equal \"_blank\" and \"rel\" attribut equal \"noopener\"", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "Site"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when \"target\" equal \"_blank\" and \"rel\" attribut equal \"noreferrer\"", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "Site"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when \"target\" equal \"_blank\" and \"rel\" attribut equal \"noreferrer\" and \"noopener\"", async function() { + const linter = createLinter({ "link-req-noopener": true }); + const html = "Site"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/spec-char-escape/__tests__/index.js b/lib/rules/spec-char-escape/__tests__/index.js index a3c92dbf0..74ac6bce6 100644 --- a/lib/rules/spec-char-escape/__tests__/index.js +++ b/lib/rules/spec-char-escape/__tests__/index.js @@ -5,48 +5,48 @@ // function createLinter() { // return new linthtml.LegacyLinter(linthtml.rules); // } -describe("spec-char-escape", function() { +// describe("spec-char-escape", function() { - // it(`Should report an error for special characters in text elements`, async function() { - // const linter = createLinter(); - // const html = `

Hello & hello

`; +// it(`Should report an error for special characters in text elements`, async function() { +// const linter = createLinter(); +// const html = `

Hello & hello

`; - // const issues = await linter.lint(html, none, { "spec-char-escape": true }); - // expect(issues).to.have.lengthOf(1); - // }); +// const issues = await linter.lint(html, none, { "spec-char-escape": true }); +// expect(issues).to.have.lengthOf(1); +// }); - // it(`Should report an error for special characters in attributes value`, async function() { - // const linter = createLinter(); - // const html = `

Foo

`; +// it(`Should report an error for special characters in attributes value`, async function() { +// const linter = createLinter(); +// const html = `

Foo

`; - // const issues = await linter.lint(html, none, { "spec-char-escape": true }); - // expect(issues).to.have.lengthOf(1); - // }); +// const issues = await linter.lint(html, none, { "spec-char-escape": true }); +// expect(issues).to.have.lengthOf(1); +// }); - // \& not working cause in js \& became \ - // /* eslint-disable no-useless-escape */ - // it(`Should not report any error for special characters in text elements`, async function() { - // const linter = createLinter(); - // const html = "

Hello \& hello

"; +// \& not working cause in js \& became \ +// /* eslint-disable no-useless-escape */ +// it(`Should not report any error for special characters in text elements`, async function() { +// const linter = createLinter(); +// const html = "

Hello \& hello

"; - // const issues = await linter.lint(html, none, { "spec-char-escape": true }); - // expect(issues).to.have.lengthOf(0); - // done(); - // }); - // }); +// const issues = await linter.lint(html, none, { "spec-char-escape": true }); +// expect(issues).to.have.lengthOf(0); +// done(); +// }); +// }); - // it(`Should not report any error for special characters in attributes value`, async function() { - // const linter = createLinter(); - // const html = "

Foo

"; +// it(`Should not report any error for special characters in attributes value`, async function() { +// const linter = createLinter(); +// const html = "

Foo

"; - // const issues = await linter.lint(html, none, { "spec-char-escape": true }); - // expect(issues).to.have.lengthOf(0); - // done(); - // }); - // }); - // /* eslint-enable no-useless-escape */ +// const issues = await linter.lint(html, none, { "spec-char-escape": true }); +// expect(issues).to.have.lengthOf(0); +// done(); +// }); +// }); +// /* eslint-enable no-useless-escape */ -}); +// }); // module.exports = [ diff --git a/lib/rules/table-req-caption/__tests__/index.js b/lib/rules/table-req-caption/__tests__/index.js index 02659d781..98786d836 100644 --- a/lib/rules/table-req-caption/__tests__/index.js +++ b/lib/rules/table-req-caption/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("table-req-caption", function() { +describe("legacy linter | table-req-caption", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when \"\" does not have a \"
\"", async function() { const linter = createLinter(); const html = "
"; @@ -46,3 +46,48 @@ describe("table-req-caption", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("table-req-caption", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when \"\" does not have a \""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report as many error as \"
\"", async function() { + const linter = createLinter({ "table-req-caption": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when \"
\" is not a child of \"\"", async function() { + const linter = createLinter({ "table-req-caption": true }); + const html = "
Hello
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when \"
\" is a sibling of \"\"", async function() { + const linter = createLinter({ "table-req-caption": true }); + const html = "
Hello
\" without \"
\"", async function() { + const linter = createLinter({ "table-req-caption": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report any error for \"\" with \"
\"", async function() { + const linter = createLinter({ "table-req-caption": true }); + const html = "
Caption>
Caption
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/table-req-header/__tests__/index.js b/lib/rules/table-req-header/__tests__/index.js index 8b17ba5d0..fea2e809e 100644 --- a/lib/rules/table-req-header/__tests__/index.js +++ b/lib/rules/table-req-header/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("table-req-header", function() { +describe("legacy linter | table-req-header", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when \"\" does not have a \"\"", async function() { const linter = createLinter(); const html = "
"; @@ -62,3 +62,63 @@ describe("table-req-header", function() { expect(issues).to.have.lengthOf(1); }); }); +describe("table-req-header", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when \"\" does not have a \"\"", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report any error for \"\" with a \"\"", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
\"", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
Header>
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for \"\" with a \"\"", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
\" (not first child)", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
DataHeader>
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for \"\" with a \"\"", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
Header>
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error for \"\" with a \"\" and text content before", async function() { + const linter = createLinter({ "table-req-header": true }); + const html = "
textHeader>
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when \"
\" not child of \"
Header>
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error when no \"
\" in first \"
Data
Header
"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); +}); diff --git a/lib/rules/tag-bans/__tests__/index.js b/lib/rules/tag-bans/__tests__/index.js index 46fd0c004..601094fb9 100644 --- a/lib/rules/tag-bans/__tests__/index.js +++ b/lib/rules/tag-bans/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("tag-bans", function() { +describe("legacy linter | tag-bans", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error for a tag named 'style'", async function() { const linter = createLinter(); const html = ""; @@ -43,3 +43,67 @@ describe("tag-bans", function() { .throw("Configuration for rule \"tag-bans\" is invalid: Expected string or string[] got boolean[]"); }); }); +describe("tag-bans", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error for a tag named 'style'", async function() { + const linter = createLinter({ + "tag-bans": [ + true, + ["style"] + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should accept a single string as option", async function() { + const linter = createLinter({ + "tag-bans": [ + true, + "style" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Banned tags should be case insensitive", async function() { + const linter = createLinter({ + "tag-bans": [ + true, + "DiV" + ] + }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should throw an error for an invalid config", function() { + const config = { + "tag-bans": [ + true, + true + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"tag-bans\" is invalid: Expected string or string[] got boolean"); + }); + + it("Should throw an error if not given a list of strings as config", function() { + const config = { + "tag-bans": [ + true, + ["string", true] + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"tag-bans\" is invalid: Expected string or string[] got boolean[]"); + }); +}); diff --git a/lib/rules/tag-bans/index.js b/lib/rules/tag-bans/index.js index c7c1ed990..0769420ef 100644 --- a/lib/rules/tag-bans/index.js +++ b/lib/rules/tag-bans/index.js @@ -31,7 +31,7 @@ function mutConfig(options) { }); } if (typeof options === "string") { - options = [options]; + options = [options.toLowerCase()]; } return options; } @@ -41,8 +41,7 @@ module.exports.lint = function(element, opts, { report }) { return; } - let banned = opts[this.name]; - banned = mutConfig(banned); + const banned = mutConfig(opts[this.name]); if (banned.indexOf(element.name) !== -1) { report({ code: "E016", diff --git a/lib/rules/tag-close/__tests__/index.js b/lib/rules/tag-close/__tests__/index.js index db30c3429..af4742cba 100644 --- a/lib/rules/tag-close/__tests__/index.js +++ b/lib/rules/tag-close/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("tag-close", function() { +describe("legacy linter | tag-close", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error for matching open/close tags", async function() { const linter = createLinter(); const html = "
"; @@ -92,3 +92,122 @@ describe("tag-close", function() { .throw("Configuration for rule \"tag-self-close\" is invalid: \"foo\" is not accepted. Accepted values are \"always\" and \"never\"."); }); }); + +describe("tag-close", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error for matching open/close tags", async function() { + const linter = createLinter({ "tag-name-match": true }); + const html = "
"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for not matching open/close tags", async function() { + const linter = createLinter({ "tag-close": true }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error for not matching open/close tags (different case)", async function() { + const linter = createLinter({ "tag-name-match": true }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error per none matching open/close tags", async function() { + const linter = createLinter({ "tag-close": true }); + const html = "

"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should report an error for not closed self closed tags when \"tag-self-close\" is set to \"always\"", async function() { + const linter = createLinter({ + "tag-self-close": [ + true, + "always" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error for closed self closed tags when \"tag-self-close\" is set to \"always\"", async function() { + const linter = createLinter({ + "tag-self-close": [ + true, + "always" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error for closed self closed tags when \"tag-self-close\" is set to \"never\"", async function() { + const linter = createLinter({ + "tag-self-close": [ + true, + "never" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report an error for not closed self closed tags when \"tag-self-close\" is set to \"never\"", async function() { + const linter = createLinter({ + "tag-self-close": [ + true, + "never" + ] + }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error for self closing tags", async function() { + const linter = createLinter({ "tag-close": true }); + const html = "

"; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report an error for unicode chars", async function() { + const linter = createLinter({ "tag-close": true }); + const html = ""; + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error for an invalid config", function() { + const config = { + "tag-self-close": [ + true, + true + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"tag-self-close\" is invalid: Expected string got boolean"); + }); + + it("Should throw an error if not given a list of strings as config", function() { + const config = { + "tag-self-close": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"tag-self-close\" is invalid: \"foo\" is not accepted. Accepted values are \"always\" and \"never\"."); + }); +}); diff --git a/lib/rules/tag-name-lowercase/__tests__/index.js b/lib/rules/tag-name-lowercase/__tests__/index.js index eb28a577c..302e00e70 100644 --- a/lib/rules/tag-name-lowercase/__tests__/index.js +++ b/lib/rules/tag-name-lowercase/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("tag-name-lowercase", function() { +describe("legacy linter | tag-name-lowercase", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should report an error when tags name are not lowercased", async function() { const linter = createLinter(); const html = ""; @@ -38,3 +38,40 @@ describe("tag-name-lowercase", function() { expect(issues).to.have.lengthOf(0); }); }); + +describe("tag-name-lowercase", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should report an error when tags name are not lowercased", async function() { + const linter = createLinter({ "tag-name-lowercase": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should report an error pers tags not lowercased", async function() { + const linter = createLinter({ "tag-name-lowercase": true }); + const html = "

"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Should not report an error when tags name are lowercased", async function() { + const linter = createLinter({ "tag-name-lowercase": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should ignore directive", async function() { + const linter = createLinter({ "tag-name-lowercase": true }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); +}); diff --git a/lib/rules/tag-req-attr/__tests__/index.js b/lib/rules/tag-req-attr/__tests__/index.js index 50d35d117..cd07fe046 100644 --- a/lib/rules/tag-req-attr/__tests__/index.js +++ b/lib/rules/tag-req-attr/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("tag-req-attr", function() { +describe("legacy linter | tag-req-attr", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when config is an empty object", async function() { const linter = createLinter(); const html = ""; @@ -63,6 +63,111 @@ describe("tag-req-attr", function() { }); }); +describe("tag-req-attr", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when config is an empty object", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + {} + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when tag contain mandatory attributes", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + { + img: [{ name: "src" }, { name: "alt" }] + } + ] + }); + const html = "\"nyan\""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should an report an error per missing attributes", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + { + img: [{ name: "src" }, { name: "alt" }] + } + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(2); + }); + + it("Mandatory attributes should not be empty by default", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + { + input: [{ name: "required" }] + } + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should not report an error for empty attribute when \"allowEmpty\" is specified", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + { + input: [{ name: "required", allowEmpty: true }] + } + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when there's no configuration for the tag", async function() { + const linter = createLinter({ + "tag-req-attr": [ + true, + { + input: [{ name: "required", allowEmpty: true }] + } + ] + }); + const html = ""; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should throw an error for an invalid config", function() { + const config = { + "tag-req-attr": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"tag-req-attr\" is invalid: Expected object got string"); + }); +}); + // module.exports = [ // { // desc: "should pass when there is no configuration for the tag", diff --git a/lib/rules/title-max-len/__tests__/index.js b/lib/rules/title-max-len/__tests__/index.js index e88da6ca3..fc170004a 100644 --- a/lib/rules/title-max-len/__tests__/index.js +++ b/lib/rules/title-max-len/__tests__/index.js @@ -2,10 +2,10 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("title-max-len", function() { +describe("legacy linter | title-max-len", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report any error when the title does exceed max length", async function() { const linter = createLinter(); const html = "Title!"; @@ -46,3 +46,71 @@ describe("title-max-len", function() { .throw("Configuration for rule \"title-max-len\" is invalid: Only positive indent value are allowed."); }); }); + +describe("title-max-len", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report any error when the title does exceed max length", async function() { + const linter = createLinter({ + "title-max-len": [ + true, + 60 + ] + }); + const html = "Title!"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should not report any error when the title length equal the max length", async function() { + const linter = createLinter({ + "title-max-len": [ + true, + 5 + ] + }); + const html = "Title"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when the title does exceed the max length", async function() { + const linter = createLinter({ + "title-max-len": [ + true, + 5 + ] + }); + const html = "Title!"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should throw an error if not given a number as config", function() { + const config = { + "title-max-len": [ + true, + "foo" + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"title-max-len\" is invalid: Expected number got string"); + }); + + it("Should throw an error if not given a positive number as config", function() { + const config = { + "title-max-len": [ + true, + -1 + ] + }; + expect(() => createLinter(config)) + .to + .throw("Configuration for rule \"title-max-len\" is invalid: Only positive indent value are allowed."); + }); +}); diff --git a/lib/rules/title-no-dup/__tests__/index.js b/lib/rules/title-no-dup/__tests__/index.js index 9a20ec820..26a759fa7 100644 --- a/lib/rules/title-no-dup/__tests__/index.js +++ b/lib/rules/title-no-dup/__tests__/index.js @@ -1,11 +1,11 @@ const { expect } = require("chai"); const linthtml = require("../../../index"); const none = require("../../../presets").presets.none; -function createLinter() { - return new linthtml.LegacyLinter(linthtml.rules); -} -describe("title-no-dup", function() { +describe("legacy linter | title-no-dup", function() { + function createLinter() { + return new linthtml.LegacyLinter(linthtml.rules); + } it("Should not report an error when title is not duplicated", async function() { const linter = createLinter(); const html = "Title!"; @@ -30,3 +30,32 @@ describe("title-no-dup", function() { expect(issues).to.have.lengthOf(3); }); }); + +describe("title-no-dup", function() { + function createLinter(rules) { + return linthtml.fromConfig({ rules }); + } + it("Should not report an error when title is not duplicated", async function() { + const linter = createLinter({ "title-no-dup": true }); + const html = "Title!"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(0); + }); + + it("Should report an error when title is duplicated", async function() { + const linter = createLinter({ "title-no-dup": true }); + const html = "Title!Title!"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(1); + }); + + it("Should catch multiple duplicates", async function() { + const linter = createLinter({ "title-no-dup": true }); + const html = "Title!Title!Title!Title!"; + + const issues = await linter.lint(html); + expect(issues).to.have.lengthOf(3); + }); +});