Commit d9c869b3 authored by David Sveningsson's avatar David Sveningsson

feat(rules): new rule `no-self-closing`

refs #58
parent f30de03e
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/no-self-closing.md inline validation: correct 1`] = `Array []`;
exports[`docs/rules/no-self-closing.md inline validation: foreign 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 5,
"context": "svg",
"line": 1,
"message": "<svg> must not be self-closed",
"offset": 4,
"ruleId": "no-self-closing",
"selector": "svg",
"severity": 2,
"size": 2,
},
],
"source": "<svg/>",
"warningCount": 0,
},
]
`;
exports[`docs/rules/no-self-closing.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 5,
"context": "div",
"line": 1,
"message": "<div> must not be self-closed",
"offset": 4,
"ruleId": "no-self-closing",
"selector": "div",
"severity": 2,
"size": 2,
},
],
"source": "<div/>",
"warningCount": 0,
},
]
`;
exports[`docs/rules/no-self-closing.md inline validation: xml 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 12,
"context": "xi:include",
"line": 1,
"message": "<xi:include> must not be self-closed",
"offset": 11,
"ruleId": "no-self-closing",
"selector": "xi:include",
"severity": 2,
"size": 2,
},
],
"source": "<xi:include/>",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<div/>`;
markup["correct"] = `<div></div>
<!-- foreign elements are ignored -->
<svg/>
<!-- elements with XML namespace are ignored -->
<xi:include/>`;
markup["foreign"] = `<svg/>`;
markup["xml"] = `<xi:include/>`;
describe("docs/rules/no-self-closing.md", () => {
it("inline validation: incorrect", () => {
const htmlvalidate = new HtmlValidate({"rules":{"no-self-closing":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
const htmlvalidate = new HtmlValidate({"rules":{"no-self-closing":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: foreign", () => {
const htmlvalidate = new HtmlValidate({"rules":{"no-self-closing":["error",{"ignoreForeign":false}]}});
const report = htmlvalidate.validateString(markup["foreign"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: xml", () => {
const htmlvalidate = new HtmlValidate({"rules":{"no-self-closing":["error",{"ignoreXML":false}]}});
const report = htmlvalidate.validateString(markup["xml"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: no-self-closing
category: style
summary: Disallow self-closing elements
---
# Disallow self-closing elements (`no-self-closing`)
Require regular end tags for elements even if the element has no content, e.g. require `<div></div>` instead of `<div/>`.
This rule has no effect on void elements, see the related rule {@link void-style}.
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="no-self-closing">
<div/>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="no-self-closing">
<div></div>
<!-- foreign elements are ignored -->
<svg/>
<!-- elements with XML namespace are ignored -->
<xi:include/>
</validate>
## Options
This rule takes an optional object:
```json
{
"ignoreForeign": true,
"ignoreXML": true
}
```
### `ignoreForeign`
By default foreign elements are ignored by this rule.
By setting `ignoreForeign` to `false` foreign elements must not be self-closed either.
<validate name="foreign" rules="no-self-closing" no-self-closing='{"ignoreForeign": false}'>
<svg/>
</validate>
### `ignoreXML`
By default elements in XML namespaces are ignored by this rule.
By setting `ignoreXML` to `false` elements in XML namespaces must not be self-closed either.
<validate name="xml" rules="no-self-closing" no-self-closing='{"ignoreXML": false}'>
<xi:include/>
</validate>
......@@ -13,7 +13,7 @@ Void elements are implicitly closed (`<img>`) but may optionally be XML-style se
This rules enforces usage of one of the two styles.
Default is to omit self-closing tag.
Non-void elements are ignored by this rule.
This rule has no effect on non-void elements, see the related rule {@link no-self-closing}.
## Rule details
......
......@@ -237,20 +237,9 @@ exports[`HTML elements <applet> valid markup 1`] = `Array []`;
exports[`HTML elements <area> invalid markup 1`] = `
Array [
Object {
"errorCount": 6,
"errorCount": 4,
"filePath": "test-files/elements/area-invalid.html",
"messages": Array [
Object {
"column": 9,
"context": undefined,
"line": 6,
"message": "End tag for <area> must be omitted",
"offset": 96,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 5,
},
Object {
"column": 9,
"context": "area",
......@@ -262,17 +251,6 @@ Array [
"severity": 2,
"size": 5,
},
Object {
"column": 3,
"context": undefined,
"line": 11,
"message": "End tag for <area> must be omitted",
"offset": 168,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 5,
},
Object {
"column": 3,
"context": "area",
......@@ -574,7 +552,7 @@ exports[`HTML elements <b> valid markup 1`] = `Array []`;
exports[`HTML elements <base> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 2,
"filePath": "test-files/elements/base-invalid.html",
"messages": Array [
Object {
......@@ -588,17 +566,6 @@ Array [
"severity": 2,
"size": 4,
},
Object {
"column": 8,
"context": undefined,
"line": 7,
"message": "End tag for <base> must be omitted",
"offset": 109,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 5,
},
Object {
"column": 8,
"context": "base",
......@@ -855,20 +822,9 @@ exports[`HTML elements <body> valid markup 1`] = `Array []`;
exports[`HTML elements <br> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 1,
"filePath": "test-files/elements/br-invalid.html",
"messages": Array [
Object {
"column": 6,
"context": undefined,
"line": 2,
"message": "End tag for <br> must be omitted",
"offset": 39,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 3,
},
Object {
"column": 6,
"context": "br",
......@@ -1162,7 +1118,7 @@ exports[`HTML elements <code> valid markup 1`] = `Array []`;
exports[`HTML elements <col> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 2,
"filePath": "test-files/elements/col-invalid.html",
"messages": Array [
Object {
......@@ -1176,17 +1132,6 @@ Array [
"severity": 2,
"size": 3,
},
Object {
"column": 9,
"context": undefined,
"line": 9,
"message": "End tag for <col> must be omitted",
"offset": 134,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 4,
},
Object {
"column": 9,
"context": "col",
......@@ -1685,20 +1630,9 @@ exports[`HTML elements <em> valid markup 1`] = `Array []`;
exports[`HTML elements <embed> invalid markup 1`] = `
Array [
Object {
"errorCount": 5,
"errorCount": 4,
"filePath": "test-files/elements/embed-invalid.html",
"messages": Array [
Object {
"column": 45,
"context": undefined,
"line": 2,
"message": "End tag for <embed> must be omitted",
"offset": 91,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 6,
},
Object {
"column": 45,
"context": "embed",
......@@ -2627,20 +2561,9 @@ exports[`HTML elements <hgroup> valid markup 1`] = `Array []`;
exports[`HTML elements <hr> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 2,
"filePath": "test-files/elements/hr-invalid.html",
"messages": Array [
Object {
"column": 6,
"context": undefined,
"line": 2,
"message": "End tag for <hr> must be omitted",
"offset": 39,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 3,
},
Object {
"column": 6,
"context": "hr",
......@@ -2889,7 +2812,7 @@ exports[`HTML elements <iframe> valid markup 1`] = `Array []`;
exports[`HTML elements <img> invalid markup 1`] = `
Array [
Object {
"errorCount": 5,
"errorCount": 4,
"filePath": "test-files/elements/img-invalid.html",
"messages": Array [
Object {
......@@ -2924,17 +2847,6 @@ Array [
"severity": 2,
"size": 3,
},
Object {
"column": 21,
"context": undefined,
"line": 8,
"message": "End tag for <img> must be omitted",
"offset": 139,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 4,
},
Object {
"column": 21,
"context": "img",
......@@ -2982,20 +2894,9 @@ exports[`HTML elements <img> valid markup 1`] = `Array []`;
exports[`HTML elements <input> invalid markup 1`] = `
Array [
Object {
"errorCount": 15,
"errorCount": 14,
"filePath": "test-files/elements/input-invalid.html",
"messages": Array [
Object {
"column": 21,
"context": undefined,
"line": 2,
"message": "End tag for <input> must be omitted",
"offset": 54,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 6,
},
Object {
"column": 21,
"context": "input",
......@@ -3360,20 +3261,9 @@ exports[`HTML elements <kbd> valid markup 1`] = `Array []`;
exports[`HTML elements <keygen> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 1,
"filePath": "test-files/elements/keygen-invalid.html",
"messages": Array [
Object {
"column": 10,
"context": undefined,
"line": 2,
"message": "End tag for <keygen> must be omitted",
"offset": 38,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 7,
},
Object {
"column": 10,
"context": "keygen",
......@@ -3523,20 +3413,9 @@ exports[`HTML elements <li> valid markup 1`] = `Array []`;
exports[`HTML elements <link> invalid markup 1`] = `
Array [
Object {
"errorCount": 6,
"errorCount": 5,
"filePath": "test-files/elements/link-invalid.html",
"messages": Array [
Object {
"column": 23,
"context": undefined,
"line": 2,
"message": "End tag for <link> must be omitted",
"offset": 56,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 5,
},
Object {
"column": 23,
"context": "link",
......@@ -3878,20 +3757,9 @@ exports[`HTML elements <math> valid markup 1`] = `Array []`;
exports[`HTML elements <meta> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 1,
"filePath": "test-files/elements/meta-invalid.html",
"messages": Array [
Object {
"column": 8,
"context": undefined,
"line": 2,
"message": "End tag for <meta> must be omitted",
"offset": 36,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 5,
},
Object {
"column": 8,
"context": "meta",
......@@ -4471,20 +4339,9 @@ exports[`HTML elements <p> valid markup 1`] = `Array []`;
exports[`HTML elements <param> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 2,
"filePath": "test-files/elements/param-invalid.html",
"messages": Array [
Object {
"column": 9,
"context": undefined,
"line": 2,
"message": "End tag for <param> must be omitted",
"offset": 37,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 6,
},
Object {
"column": 9,
"context": "param",
......@@ -5015,11 +4872,11 @@ Array [
},
Object {
"column": 8,
"context": undefined,
"context": "script",
"line": 8,
"message": "End tag for <script> must not be omitted",
"message": "<script> must not be self-closed",
"offset": 330,
"ruleId": "void",
"ruleId": "no-self-closing",
"selector": "script:nth-child(3)",
"severity": 2,
"size": 2,
......@@ -5150,20 +5007,9 @@ exports[`HTML elements <small> valid markup 1`] = `Array []`;
exports[`HTML elements <source> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 2,
"filePath": "test-files/elements/source-invalid.html",
"messages": Array [
Object {
"column": 11,
"context": undefined,
"line": 3,
"message": "End tag for <source> must be omitted",
"offset": 47,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 7,
},
Object {
"column": 11,
"context": "source",
......@@ -6106,20 +5952,9 @@ exports[`HTML elements <tr> valid markup 1`] = `Array []`;
exports[`HTML elements <track> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 1,
"filePath": "test-files/elements/track-invalid.html",
"messages": Array [
Object {
"column": 10,
"context": undefined,
"line": 3,
"message": "End tag for <track> must be omitted",
"offset": 46,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 6,
},
Object {
"column": 10,
"context": "track",
......@@ -6389,20 +6224,9 @@ exports[`HTML elements <video> valid markup 1`] = `Array []`;
exports[`HTML elements <wbr> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 1,
"filePath": "test-files/elements/wbr-invalid.html",
"messages": Array [
Object {
"column": 7,
"context": undefined,
"line": 2,
"message": "End tag for <wbr> must be omitted",
"offset": 35,
"ruleId": "void",
"selector": null,
"severity": 2,
"size": 4,
},
Object {
"column": 7,
"context": "wbr",
......
......@@ -164,6 +164,9 @@ describe("HTML elements", () => {
* yield any errors */
"prefer-button": "off",
/* void is being deprecated */
void: "off",
/* none of the WCAG rules should trigger in these tests, they are tested
* separately and adds too much noise here */
"wcag/h32": "off",
......
......@@ -10,7 +10,7 @@ Object {
"rules": Object {
"deprecated": "error",
"element-permitted-content": "off",
"void": "error",
"no-self-closing": "error",
},
"transform": Object {},
}
......@@ -24,22 +24,22 @@ Array [
"messages": Array [
Object {
"column": 3,
"context": undefined,
"context": "i",
"line": 1,
"message": "End tag for <i> must not be omitted",
"message": "<i> must not be self-closed",
"offset": 2,
"ruleId": "void",
"ruleId": "no-self-closing",
"selector": "i",
"severity": 2,
"size": 2,
},
Object {
"column": 3,
"context": undefined,
"context": "i",
"line": 8,
"message": "End tag for <i> must not be omitted",
"offset": 190,
"ruleId": "void",
"message": "<i> must not be self-closed",
"offset": 212,
"ruleId": "no-self-closing",
"selector": "i:nth-child(4)",
"severity": 2,
"size": 2,
......@@ -47,11 +47,11 @@ Array [
],
"source": "<i/>Before disable, should trigger
<!-- [html-validate-disable void] -->
<!-- [html-validate-disable no-self-closing] -->
<i/>After disable, should not trigger
<i/>After disable, should not trigger